From 0dbfaec4213ece546e91cdfb3cb882ba0c147c47 Mon Sep 17 00:00:00 2001 From: ProGuard Date: Mon, 18 Mar 2013 12:50:00 +0100 Subject: [PATCH] Created ProGuard version 4.9. --- README | 33 + bin/proguard.bat | 14 + bin/proguard.sh | 12 + bin/proguardgui.bat | 14 + bin/proguardgui.sh | 15 + bin/retrace.bat | 14 + bin/retrace.sh | 12 + build/README | 40 + build/build.properties | 4 + build/build.sh | 110 + build/build.xml | 214 + build/makefile | 107 + build/maven/ant/pom.xml | 78 + build/maven/base/pom.xml | 69 + build/maven/gradle/pom.xml | 85 + build/maven/gui/pom.xml | 78 + build/maven/pom.xml | 162 + build/maven/retrace/pom.xml | 61 + build/maven/wtk/pom.xml | 79 + docs/FAQ.html | 278 + docs/GPL.html | 406 ++ docs/GPL_exception.html | 57 + docs/acknowledgements.html | 88 + docs/alternatives.html | 739 +++ docs/android_shades.png | Bin 0 -> 2726 bytes docs/checkmark.gif | Bin 0 -> 63 bytes docs/dexguard.png | Bin 0 -> 5331 bytes docs/downloads.html | 692 +++ docs/drop1.gif | Bin 0 -> 803 bytes docs/drop2.gif | Bin 0 -> 620 bytes docs/drop3.gif | Bin 0 -> 175 bytes docs/favicon.ico | Bin 0 -> 7406 bytes docs/feedback.html | 118 + docs/index.html | 92 + docs/license.html | 61 + docs/main.html | 104 + docs/manual/ant.html | 644 +++ docs/manual/attention.gif | Bin 0 -> 896 bytes docs/manual/examples.html | 1582 +++++ docs/manual/gradle.html | 545 ++ docs/manual/gui.html | 481 ++ docs/manual/index.html | 52 + docs/manual/introduction.html | 172 + docs/manual/limitations.html | 69 + docs/manual/optimizations.html | 198 + docs/manual/refcard.html | 486 ++ docs/manual/retrace/examples.html | 345 ++ docs/manual/retrace/index.html | 37 + docs/manual/retrace/introduction.html | 79 + docs/manual/retrace/usage.html | 128 + docs/manual/sections.html | 54 + docs/manual/style.css | 129 + docs/manual/troubleshooting.html | 824 +++ docs/manual/usage.html | 1246 ++++ docs/manual/wtk.html | 70 + docs/quality.html | 56 + docs/results.html | 169 + docs/saikoalogo.png | Bin 0 -> 2901 bytes docs/screenshot_console.gif | Bin 0 -> 18930 bytes docs/screenshot_console_small.gif | Bin 0 -> 19730 bytes docs/screenshot_gui1.gif | Bin 0 -> 42755 bytes docs/screenshot_gui2.gif | Bin 0 -> 36887 bytes docs/screenshot_gui3.gif | Bin 0 -> 37080 bytes docs/screenshot_gui4.gif | Bin 0 -> 40769 bytes docs/screenshot_gui5.gif | Bin 0 -> 35909 bytes docs/screenshot_gui6.gif | Bin 0 -> 37123 bytes docs/screenshot_gui7.gif | Bin 0 -> 41922 bytes docs/screenshot_gui8.gif | Bin 0 -> 31454 bytes docs/screenshots.html | 67 + docs/screenshots_gui_small.gif | Bin 0 -> 161022 bytes docs/sections.html | 57 + docs/sflogo.png | Bin 0 -> 469 bytes docs/steel.gif | Bin 0 -> 2759 bytes docs/style.css | 224 + docs/testimonials.html | 133 + docs/title.gif | Bin 0 -> 2613 bytes docs/title.html | 17 + examples/android.pro | 168 + examples/annotations/examples.pro | 60 + examples/annotations/examples/Applet.java | 22 + .../annotations/examples/Application.java | 20 + examples/annotations/examples/Bean.java | 56 + .../annotations/examples/NativeCallBack.java | 44 + examples/annotations/lib/annotations.jar | Bin 0 -> 6126 bytes examples/annotations/lib/annotations.pro | 118 + .../src/proguard/annotation/Keep.java | 18 + .../proguard/annotation/KeepApplication.java | 18 + .../annotation/KeepClassMemberNames.java | 18 + .../proguard/annotation/KeepClassMembers.java | 18 + .../annotation/KeepGettersSetters.java | 18 + .../annotation/KeepImplementations.java | 18 + .../src/proguard/annotation/KeepName.java | 18 + .../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 | 88 + examples/ant/applications1.xml | 15 + examples/ant/applications2.xml | 74 + examples/ant/applications3.xml | 98 + examples/ant/library.xml | 102 + examples/ant/midlets.xml | 52 + examples/ant/proguard.xml | 88 + examples/ant/servlets.xml | 88 + examples/applets.pro | 69 + examples/applications.pro | 75 + examples/dictionaries/compact.txt | 18 + examples/dictionaries/keywords.txt | 58 + examples/dictionaries/shakespeare.txt | 23 + examples/dictionaries/windows.txt | 209 + examples/gradle/android.gradle | 189 + examples/gradle/applets.gradle | 90 + examples/gradle/applications.gradle | 96 + examples/gradle/library.gradle | 99 + examples/gradle/midlets.gradle | 88 + examples/gradle/proguard.gradle | 91 + examples/gradle/proguardall.gradle | 93 + examples/gradle/proguardgui.gradle | 72 + examples/gradle/retrace.gradle | 64 + examples/gradle/scala.gradle | 153 + examples/gradle/servlets.gradle | 91 + examples/library.pro | 79 + examples/midlets.pro | 67 + examples/proguard.pro | 70 + examples/proguardall.pro | 72 + examples/proguardgui.pro | 51 + examples/retrace.pro | 43 + examples/scala.pro | 132 + examples/servlets.pro | 70 + src/proguard/ArgumentWordReader.java | 111 + src/proguard/ClassPath.java | 94 + src/proguard/ClassPathEntry.java | 282 + src/proguard/ClassSpecification.java | 259 + .../ClassSpecificationVisitorFactory.java | 503 ++ src/proguard/Configuration.java | 331 ++ src/proguard/ConfigurationConstants.java | 123 + src/proguard/ConfigurationParser.java | 1347 +++++ src/proguard/ConfigurationWriter.java | 651 +++ src/proguard/DataEntryReaderFactory.java | 141 + src/proguard/DataEntryWriterFactory.java | 150 + src/proguard/DescriptorKeepChecker.java | 169 + src/proguard/DuplicateClassPrinter.java | 63 + src/proguard/FileWordReader.java | 55 + .../FullyQualifiedClassNameChecker.java | 191 + src/proguard/GPL.java | 201 + src/proguard/Initializer.java | 433 ++ src/proguard/InputReader.java | 233 + src/proguard/KeepClassMemberChecker.java | 87 + src/proguard/KeepClassSpecification.java | 137 + src/proguard/LineWordReader.java | 74 + src/proguard/MANIFEST.MF | 2 + src/proguard/MemberSpecification.java | 114 + src/proguard/OutputWriter.java | 296 + src/proguard/ParseException.java | 51 + src/proguard/ProGuard.java | 504 ++ src/proguard/SeedPrinter.java | 97 + src/proguard/SubclassedClassFilter.java | 62 + src/proguard/Targeter.java | 88 + src/proguard/UpToDateChecker.java | 232 + src/proguard/WordReader.java | 387 ++ src/proguard/ant/ClassPathElement.java | 191 + .../ant/ClassSpecificationElement.java | 258 + src/proguard/ant/ConfigurationElement.java | 124 + src/proguard/ant/ConfigurationTask.java | 450 ++ src/proguard/ant/FilterElement.java | 85 + .../ant/KeepSpecificationElement.java | 87 + .../ant/MemberSpecificationElement.java | 218 + src/proguard/ant/ProGuardTask.java | 357 ++ src/proguard/ant/package.html | 3 + src/proguard/ant/task.properties | 2 + src/proguard/classfile/ClassConstants.java | 329 ++ src/proguard/classfile/ClassPool.java | 161 + src/proguard/classfile/Clazz.java | 261 + src/proguard/classfile/Field.java | 32 + src/proguard/classfile/LibraryClass.java | 548 ++ src/proguard/classfile/LibraryField.java | 77 + src/proguard/classfile/LibraryMember.java | 108 + src/proguard/classfile/LibraryMethod.java | 83 + src/proguard/classfile/Member.java | 57 + src/proguard/classfile/Method.java | 32 + src/proguard/classfile/ProgramClass.java | 572 ++ src/proguard/classfile/ProgramField.java | 93 + src/proguard/classfile/ProgramMember.java | 140 + src/proguard/classfile/ProgramMethod.java | 99 + src/proguard/classfile/VisitorAccepter.java | 47 + .../classfile/attribute/Attribute.java | 142 + .../attribute/BootstrapMethodInfo.java | 89 + .../attribute/BootstrapMethodsAttribute.java | 95 + .../classfile/attribute/CodeAttribute.java | 202 + .../attribute/ConstantValueAttribute.java | 62 + .../attribute/DeprecatedAttribute.java | 66 + .../attribute/EnclosingMethodAttribute.java | 132 + .../classfile/attribute/ExceptionInfo.java | 100 + .../attribute/ExceptionsAttribute.java | 80 + .../attribute/InnerClassesAttribute.java | 80 + .../classfile/attribute/InnerClassesInfo.java | 119 + .../classfile/attribute/LineNumberInfo.java | 50 + .../attribute/LineNumberTableAttribute.java | 150 + .../attribute/LocalVariableInfo.java | 117 + .../LocalVariableTableAttribute.java | 79 + .../attribute/LocalVariableTypeInfo.java | 125 + .../LocalVariableTypeTableAttribute.java | 79 + .../attribute/SignatureAttribute.java | 100 + .../attribute/SourceDirAttribute.java | 62 + .../attribute/SourceFileAttribute.java | 62 + .../attribute/SyntheticAttribute.java | 66 + .../classfile/attribute/UnknownAttribute.java | 82 + .../attribute/annotation/Annotation.java | 143 + .../AnnotationDefaultAttribute.java | 73 + .../annotation/AnnotationElementValue.java | 76 + .../annotation/AnnotationsAttribute.java | 100 + .../annotation/ArrayElementValue.java | 82 + .../annotation/ClassElementValue.java | 95 + .../annotation/ConstantElementValue.java | 71 + .../attribute/annotation/ElementValue.java | 126 + .../annotation/EnumConstantElementValue.java | 138 + .../ParameterAnnotationsAttribute.java | 83 + .../RuntimeInvisibleAnnotationsAttribute.java | 70 + ...nvisibleParameterAnnotationsAttribute.java | 62 + .../RuntimeVisibleAnnotationsAttribute.java | 70 + ...eVisibleParameterAnnotationsAttribute.java | 62 + .../attribute/annotation/package.html | 4 + .../visitor/AllAnnotationVisitor.java | 105 + .../visitor/AllElementValueVisitor.java | 201 + .../visitor/AnnotatedClassVisitor.java | 62 + .../visitor/AnnotationToMemberVisitor.java | 62 + .../visitor/AnnotationTypeFilter.java | 101 + .../annotation/visitor/AnnotationVisitor.java | 40 + .../visitor/ElementValueVisitor.java | 51 + .../attribute/annotation/visitor/package.html | 3 + src/proguard/classfile/attribute/package.html | 3 + .../attribute/preverification/DoubleType.java | 66 + .../attribute/preverification/FloatType.java | 66 + .../attribute/preverification/FullFrame.java | 202 + .../preverification/IntegerType.java | 66 + .../preverification/LessZeroFrame.java | 103 + .../attribute/preverification/LongType.java | 66 + .../preverification/MoreZeroFrame.java | 161 + .../attribute/preverification/NullType.java | 66 + .../attribute/preverification/ObjectType.java | 107 + .../preverification/SameOneFrame.java | 115 + .../preverification/SameZeroFrame.java | 74 + .../preverification/StackMapAttribute.java | 91 + .../preverification/StackMapFrame.java | 117 + .../StackMapTableAttribute.java | 93 + .../attribute/preverification/TopType.java | 66 + .../UninitializedThisType.java | 66 + .../preverification/UninitializedType.java | 106 + .../preverification/VerificationType.java | 103 + .../VerificationTypeFactory.java | 112 + .../visitor/StackMapFrameVisitor.java | 40 + .../visitor/VerificationTypeVisitor.java | 65 + .../visitor/AllAttributeVisitor.java | 117 + .../AllBootstrapMethodInfoVisitor.java | 55 + .../visitor/AllExceptionInfoVisitor.java | 55 + .../visitor/AllInnerClassesInfoVisitor.java | 55 + .../visitor/AttributeNameFilter.java | 386 ++ .../attribute/visitor/AttributeVisitor.java | 90 + .../visitor/BootstrapMethodInfoVisitor.java | 40 + .../visitor/ExceptionInfoVisitor.java | 37 + .../visitor/InnerClassesInfoVisitor.java | 38 + .../visitor/LineNumberInfoVisitor.java | 38 + .../visitor/LocalVariableInfoVisitor.java | 38 + .../visitor/LocalVariableTypeInfoVisitor.java | 38 + .../visitor/MultiAttributeVisitor.java | 365 ++ .../visitor/NonEmptyAttributeFilter.java | 293 + .../visitor/RequiredAttributeFilter.java | 362 ++ .../attribute/visitor/StackSizeComputer.java | 378 ++ .../classfile/attribute/visitor/package.html | 3 + .../classfile/constant/ClassConstant.java | 105 + src/proguard/classfile/constant/Constant.java | 68 + .../classfile/constant/DoubleConstant.java | 82 + .../classfile/constant/FieldrefConstant.java | 71 + .../classfile/constant/FloatConstant.java | 82 + .../classfile/constant/IntegerConstant.java | 82 + .../constant/InterfaceMethodrefConstant.java | 71 + .../constant/InvokeDynamicConstant.java | 148 + .../classfile/constant/LongConstant.java | 82 + .../constant/MethodHandleConstant.java | 124 + .../constant/MethodTypeConstant.java | 93 + .../classfile/constant/MethodrefConstant.java | 71 + .../constant/NameAndTypeConstant.java | 119 + .../classfile/constant/RefConstant.java | 130 + .../classfile/constant/StringConstant.java | 135 + .../classfile/constant/Utf8Constant.java | 285 + .../constant/visitor/AllConstantVisitor.java | 53 + .../BootstrapMethodHandleTraveler.java | 100 + .../constant/visitor/ConstantTagFilter.java | 86 + .../constant/visitor/ConstantVisitor.java | 49 + .../visitor/ExceptClassConstantFilter.java | 69 + .../constant/visitor/MethodrefTraveler.java | 60 + .../classfile/constant/visitor/package.html | 3 + .../classfile/editor/AccessFixer.java | 180 + .../classfile/editor/AnnotationAdder.java | 153 + .../editor/AnnotationsAttributeEditor.java | 67 + .../classfile/editor/AttributeAdder.java | 455 ++ .../classfile/editor/AttributeSorter.java | 89 + .../classfile/editor/AttributesEditor.java | 269 + .../classfile/editor/BridgeMethodFixer.java | 117 + .../classfile/editor/ClassEditor.java | 255 + .../classfile/editor/ClassElementSorter.java | 52 + .../classfile/editor/ClassMemberSorter.java | 69 + .../classfile/editor/ClassReferenceFixer.java | 546 ++ .../editor/CodeAttributeComposer.java | 918 +++ .../classfile/editor/CodeAttributeEditor.java | 1199 ++++ .../editor/CodeAttributeEditorResetter.java | 60 + .../classfile/editor/ComparableConstant.java | 249 + .../classfile/editor/ConstantAdder.java | 239 + .../classfile/editor/ConstantPoolEditor.java | 782 +++ .../editor/ConstantPoolRemapper.java | 662 +++ .../editor/ConstantPoolShrinker.java | 578 ++ .../classfile/editor/ConstantPoolSorter.java | 123 + .../classfile/editor/ElementValueAdder.java | 217 + .../classfile/editor/ElementValuesEditor.java | 238 + .../classfile/editor/ExceptionAdder.java | 65 + .../classfile/editor/ExceptionInfoAdder.java | 67 + .../editor/ExceptionsAttributeEditor.java | 68 + .../editor/InnerClassesAccessFixer.java | 83 + .../classfile/editor/InstructionAdder.java | 76 + .../classfile/editor/InstructionWriter.java | 320 ++ .../classfile/editor/InterfaceAdder.java | 62 + .../classfile/editor/InterfaceSorter.java | 152 + .../classfile/editor/InterfacesEditor.java | 122 + .../classfile/editor/LineNumberInfoAdder.java | 59 + .../LineNumberTableAttributeEditor.java | 67 + .../editor/LocalVariableInfoAdder.java | 67 + .../LocalVariableTableAttributeEditor.java | 67 + .../editor/LocalVariableTypeInfoAdder.java | 68 + ...LocalVariableTypeTableAttributeEditor.java | 68 + .../classfile/editor/MemberAdder.java | 294 + .../editor/MemberReferenceFixer.java | 447 ++ .../editor/MethodInvocationFixer.java | 243 + .../classfile/editor/NameAndTypeShrinker.java | 188 + .../editor/NamedAttributeDeleter.java | 54 + .../ParameterAnnotationsAttributeEditor.java | 71 + .../classfile/editor/StackSizeUpdater.java | 54 + .../classfile/editor/SubclassAdder.java | 59 + .../classfile/editor/SubclassToAdder.java | 60 + .../classfile/editor/Utf8Shrinker.java | 455 ++ .../classfile/editor/VariableCleaner.java | 271 + .../classfile/editor/VariableEditor.java | 130 + .../classfile/editor/VariableRemapper.java | 156 + .../classfile/editor/VariableSizeUpdater.java | 105 + src/proguard/classfile/editor/package.html | 3 + .../instruction/BranchInstruction.java | 179 + .../instruction/ConstantInstruction.java | 309 + .../classfile/instruction/Instruction.java | 920 +++ .../instruction/InstructionConstants.java | 449 ++ .../instruction/InstructionFactory.java | 300 + .../instruction/InstructionUtil.java | 67 + .../instruction/LookUpSwitchInstruction.java | 135 + .../instruction/SimpleInstruction.java | 255 + .../instruction/SwitchInstruction.java | 83 + .../instruction/TableSwitchInstruction.java | 139 + .../instruction/VariableInstruction.java | 372 ++ .../classfile/instruction/package.html | 9 + .../visitor/AllInstructionVisitor.java | 56 + .../visitor/InstructionConstantVisitor.java | 65 + .../visitor/InstructionCounter.java | 59 + .../visitor/InstructionVisitor.java | 42 + .../visitor/MultiInstructionVisitor.java | 131 + .../instruction/visitor/package.html | 3 + .../classfile/io/LibraryClassReader.java | 383 ++ .../classfile/io/ProgramClassReader.java | 919 +++ .../classfile/io/ProgramClassWriter.java | 735 +++ .../classfile/io/RuntimeDataInput.java | 223 + .../classfile/io/RuntimeDataOutput.java | 224 + src/proguard/classfile/io/package.html | 3 + src/proguard/classfile/package.html | 15 + src/proguard/classfile/util/AccessUtil.java | 105 + .../util/ClassReferenceInitializer.java | 559 ++ .../util/ClassSubHierarchyInitializer.java | 77 + .../util/ClassSuperHierarchyInitializer.java | 163 + src/proguard/classfile/util/ClassUtil.java | 1227 ++++ .../util/DescriptorClassEnumeration.java | 236 + .../DynamicClassReferenceInitializer.java | 485 ++ .../DynamicMemberReferenceInitializer.java | 943 +++ .../util/EnumFieldReferenceInitializer.java | 150 + .../util/ExternalTypeEnumeration.java | 106 + .../util/InstructionSequenceMatcher.java | 754 +++ .../util/InternalTypeEnumeration.java | 204 + src/proguard/classfile/util/MemberFinder.java | 197 + src/proguard/classfile/util/MethodLinker.java | 160 + .../classfile/util/SimplifiedVisitor.java | 834 +++ .../util/StringReferenceInitializer.java | 90 + src/proguard/classfile/util/StringSharer.java | 172 + .../classfile/util/WarningPrinter.java | 136 + src/proguard/classfile/util/package.html | 3 + .../classfile/visitor/AllClassVisitor.java | 47 + .../classfile/visitor/AllFieldVisitor.java | 55 + .../classfile/visitor/AllMemberVisitor.java | 57 + .../classfile/visitor/AllMethodVisitor.java | 55 + .../classfile/visitor/BottomClassFilter.java | 69 + .../classfile/visitor/ClassAccessFilter.java | 88 + .../classfile/visitor/ClassCleaner.java | 275 + .../classfile/visitor/ClassCollector.java | 58 + .../classfile/visitor/ClassCounter.java | 56 + .../visitor/ClassHierarchyTraveler.java | 91 + .../classfile/visitor/ClassNameFilter.java | 112 + .../classfile/visitor/ClassPoolFiller.java | 55 + .../classfile/visitor/ClassPoolVisitor.java | 37 + .../visitor/ClassPresenceFilter.java | 93 + .../classfile/visitor/ClassPrinter.java | 1012 ++++ .../classfile/visitor/ClassVersionFilter.java | 85 + .../classfile/visitor/ClassVersionSetter.java | 83 + .../classfile/visitor/ClassVisitor.java | 36 + .../visitor/ConcreteClassDownTraveler.java | 100 + .../visitor/DotClassClassVisitor.java | 89 + .../classfile/visitor/ExceptClassFilter.java | 69 + .../visitor/ExceptClassesFilter.java | 90 + .../classfile/visitor/ExceptionCounter.java | 52 + .../ExceptionExcludedOffsetFilter.java | 64 + .../ExceptionHandlerConstantVisitor.java | 62 + .../visitor/ExceptionHandlerFilter.java | 70 + .../visitor/ExceptionOffsetFilter.java | 64 + .../visitor/ExceptionRangeFilter.java | 68 + .../ImplementedClassConstantFilter.java | 69 + .../visitor/ImplementedClassFilter.java | 71 + .../ImplementingClassConstantFilter.java | 70 + .../classfile/visitor/LibraryClassFilter.java | 60 + .../visitor/LibraryMemberFilter.java | 73 + .../classfile/visitor/MemberAccessFilter.java | 122 + .../visitor/MemberClassAccessFilter.java | 106 + .../classfile/visitor/MemberCollector.java | 59 + .../classfile/visitor/MemberCounter.java | 72 + .../visitor/MemberDescriptorFilter.java | 113 + .../classfile/visitor/MemberNameFilter.java | 114 + .../visitor/MemberToClassVisitor.java | 90 + .../classfile/visitor/MemberVisitor.java | 40 + .../visitor/MethodImplementationFilter.java | 70 + .../visitor/MethodImplementationTraveler.java | 128 + .../visitor/MultiClassPoolVisitor.java | 88 + .../classfile/visitor/MultiClassVisitor.java | 97 + .../classfile/visitor/MultiMemberVisitor.java | 113 + .../classfile/visitor/NamedClassVisitor.java | 49 + .../classfile/visitor/NamedFieldVisitor.java | 61 + .../classfile/visitor/NamedMethodVisitor.java | 61 + .../classfile/visitor/ProgramClassFilter.java | 60 + .../visitor/ProgramMemberFilter.java | 73 + .../visitor/ReferencedClassVisitor.java | 255 + .../visitor/ReferencedMemberVisitor.java | 73 + .../visitor/SimilarMemberVisitor.java | 125 + .../classfile/visitor/SimpleClassPrinter.java | 167 + .../classfile/visitor/SubclassFilter.java | 91 + .../classfile/visitor/SubclassTraveler.java | 60 + .../visitor/VariableClassVisitor.java | 78 + .../visitor/VariableMemberVisitor.java | 96 + src/proguard/classfile/visitor/package.html | 40 + src/proguard/evaluation/BasicBranchUnit.java | 126 + .../evaluation/BasicInvocationUnit.java | 425 ++ src/proguard/evaluation/BranchUnit.java | 63 + .../evaluation/ClassConstantValueFactory.java | 53 + .../evaluation/ConstantValueFactory.java | 113 + src/proguard/evaluation/InvocationUnit.java | 62 + src/proguard/evaluation/Processor.java | 908 +++ src/proguard/evaluation/Stack.java | 560 ++ src/proguard/evaluation/TracedStack.java | 342 ++ src/proguard/evaluation/TracedVariables.java | 199 + src/proguard/evaluation/Variables.java | 347 ++ .../evaluation/value/Category1Value.java | 41 + .../evaluation/value/Category2Value.java | 41 + .../evaluation/value/ComparisonValue.java | 69 + .../value/CompositeDoubleValue.java | 81 + .../evaluation/value/CompositeFloatValue.java | 81 + .../value/CompositeIntegerValue.java | 87 + .../evaluation/value/CompositeLongValue.java | 87 + .../evaluation/value/ConvertedByteValue.java | 64 + .../value/ConvertedCharacterValue.java | 64 + .../value/ConvertedDoubleValue.java | 64 + .../evaluation/value/ConvertedFloatValue.java | 64 + .../value/ConvertedIntegerValue.java | 64 + .../evaluation/value/ConvertedLongValue.java | 64 + .../evaluation/value/ConvertedShortValue.java | 64 + .../evaluation/value/DoubleValue.java | 359 ++ src/proguard/evaluation/value/FloatValue.java | 359 ++ .../value/IdentifiedDoubleValue.java | 67 + .../value/IdentifiedFloatValue.java | 67 + .../value/IdentifiedIntegerValue.java | 67 + .../evaluation/value/IdentifiedLongValue.java | 67 + .../value/IdentifiedReferenceValue.java | 102 + .../value/IdentifiedValueFactory.java | 75 + .../value/InstructionOffsetValue.java | 307 + .../evaluation/value/IntegerValue.java | 1002 ++++ src/proguard/evaluation/value/LongValue.java | 554 ++ .../evaluation/value/NegatedDoubleValue.java | 71 + .../evaluation/value/NegatedFloatValue.java | 71 + .../evaluation/value/NegatedIntegerValue.java | 71 + .../evaluation/value/NegatedLongValue.java | 71 + .../value/ParticularDoubleValue.java | 221 + .../value/ParticularFloatValue.java | 221 + .../value/ParticularIntegerValue.java | 383 ++ .../evaluation/value/ParticularLongValue.java | 271 + .../evaluation/value/ReferenceValue.java | 540 ++ .../evaluation/value/SpecificDoubleValue.java | 186 + .../evaluation/value/SpecificFloatValue.java | 186 + .../value/SpecificIntegerValue.java | 354 ++ .../evaluation/value/SpecificLongValue.java | 259 + .../value/SpecificValueFactory.java | 97 + src/proguard/evaluation/value/TopValue.java | 79 + .../evaluation/value/UnknownDoubleValue.java | 125 + .../evaluation/value/UnknownFloatValue.java | 125 + .../evaluation/value/UnknownIntegerValue.java | 216 + .../evaluation/value/UnknownLongValue.java | 160 + src/proguard/evaluation/value/Value.java | 169 + .../evaluation/value/ValueFactory.java | 193 + src/proguard/evaluation/value/package.html | 3 + src/proguard/gradle/ProGuardTask.java | 1416 +++++ src/proguard/gui/ClassPathPanel.java | 441 ++ .../gui/ClassSpecificationDialog.java | 546 ++ .../gui/ClassSpecificationsPanel.java | 231 + src/proguard/gui/ExtensionFileFilter.java | 78 + src/proguard/gui/FilterBuilder.java | 208 + src/proguard/gui/FilterDialog.java | 320 ++ src/proguard/gui/GUIResources.java | 56 + src/proguard/gui/GUIResources.properties | 643 +++ src/proguard/gui/KeepSpecificationsPanel.java | 81 + src/proguard/gui/ListPanel.java | 341 ++ src/proguard/gui/MANIFEST.MF | 3 + .../gui/MemberSpecificationDialog.java | 509 ++ .../gui/MemberSpecificationsPanel.java | 304 + src/proguard/gui/MessageDialogRunnable.java | 90 + src/proguard/gui/OptimizationsDialog.java | 251 + src/proguard/gui/ProGuardGUI.java | 1771 ++++++ src/proguard/gui/ProGuardRunnable.java | 154 + src/proguard/gui/ReTraceRunnable.java | 149 + src/proguard/gui/SwingUtil.java | 82 + src/proguard/gui/TabbedPane.java | 229 + src/proguard/gui/TextAreaOutputStream.java | 81 + src/proguard/gui/arrow.gif | Bin 0 -> 112 bytes src/proguard/gui/boilerplate.pro | 384 ++ src/proguard/gui/default.pro | 292 + src/proguard/gui/package.html | 3 + src/proguard/gui/splash/BufferedSprite.java | 145 + src/proguard/gui/splash/CircleSprite.java | 74 + src/proguard/gui/splash/ClipSprite.java | 85 + src/proguard/gui/splash/ColorSprite.java | 65 + src/proguard/gui/splash/CompositeSprite.java | 56 + src/proguard/gui/splash/ConstantColor.java | 51 + src/proguard/gui/splash/ConstantDouble.java | 49 + src/proguard/gui/splash/ConstantFont.java | 46 + src/proguard/gui/splash/ConstantInt.java | 49 + src/proguard/gui/splash/ConstantString.java | 49 + src/proguard/gui/splash/ConstantTiming.java | 57 + src/proguard/gui/splash/FontSprite.java | 65 + src/proguard/gui/splash/ImageSprite.java | 76 + src/proguard/gui/splash/LinearColor.java | 72 + src/proguard/gui/splash/LinearDouble.java | 55 + src/proguard/gui/splash/LinearInt.java | 55 + src/proguard/gui/splash/LinearTiming.java | 55 + .../gui/splash/OverrideGraphics2D.java | 598 ++ src/proguard/gui/splash/RectangleSprite.java | 114 + src/proguard/gui/splash/SawToothTiming.java | 53 + src/proguard/gui/splash/ShadowedSprite.java | 109 + src/proguard/gui/splash/SineTiming.java | 53 + src/proguard/gui/splash/SmoothTiming.java | 66 + src/proguard/gui/splash/SplashPanel.java | 235 + src/proguard/gui/splash/Sprite.java | 41 + src/proguard/gui/splash/TextSprite.java | 89 + src/proguard/gui/splash/TimeSwitchSprite.java | 75 + src/proguard/gui/splash/Timing.java | 34 + src/proguard/gui/splash/TypeWriterString.java | 71 + src/proguard/gui/splash/VariableColor.java | 36 + src/proguard/gui/splash/VariableDouble.java | 34 + src/proguard/gui/splash/VariableFont.java | 36 + src/proguard/gui/splash/VariableInt.java | 34 + src/proguard/gui/splash/VariableSizeFont.java | 65 + src/proguard/gui/splash/VariableString.java | 34 + src/proguard/gui/splash/package.html | 4 + src/proguard/gui/vtitle.png | Bin 0 -> 23313 bytes src/proguard/io/CascadingDataEntryWriter.java | 94 + src/proguard/io/ClassFilter.java | 59 + src/proguard/io/ClassReader.java | 115 + src/proguard/io/ClassRewriter.java | 80 + src/proguard/io/DataEntry.java | 62 + src/proguard/io/DataEntryClassWriter.java | 85 + src/proguard/io/DataEntryCopier.java | 247 + src/proguard/io/DataEntryDirectoryFilter.java | 40 + src/proguard/io/DataEntryFilter.java | 38 + src/proguard/io/DataEntryNameFilter.java | 54 + src/proguard/io/DataEntryObfuscator.java | 150 + src/proguard/io/DataEntryParentFilter.java | 51 + src/proguard/io/DataEntryPump.java | 43 + src/proguard/io/DataEntryReader.java | 38 + src/proguard/io/DataEntryRenamer.java | 104 + src/proguard/io/DataEntryRewriter.java | 148 + src/proguard/io/DataEntryWriter.java | 73 + src/proguard/io/DirectoryFilter.java | 58 + src/proguard/io/DirectoryPump.java | 78 + src/proguard/io/DirectoryWriter.java | 165 + src/proguard/io/FileDataEntry.java | 96 + src/proguard/io/FilteredDataEntryReader.java | 90 + src/proguard/io/FilteredDataEntryWriter.java | 125 + src/proguard/io/Finisher.java | 37 + src/proguard/io/JarReader.java | 75 + src/proguard/io/JarWriter.java | 235 + src/proguard/io/ManifestRewriter.java | 211 + src/proguard/io/NameFilter.java | 83 + src/proguard/io/ParentDataEntryWriter.java | 75 + src/proguard/io/RenamedDataEntry.java | 83 + src/proguard/io/ZipDataEntry.java | 98 + src/proguard/io/package.html | 4 + src/proguard/obfuscate/AttributeShrinker.java | 120 + .../obfuscate/AttributeUsageMarker.java | 71 + src/proguard/obfuscate/ClassObfuscator.java | 541 ++ src/proguard/obfuscate/ClassRenamer.java | 109 + .../obfuscate/DictionaryNameFactory.java | 189 + src/proguard/obfuscate/MapCleaner.java | 57 + src/proguard/obfuscate/MappingKeeper.java | 177 + src/proguard/obfuscate/MappingPrinter.java | 147 + src/proguard/obfuscate/MappingProcessor.java | 79 + src/proguard/obfuscate/MappingReader.java | 199 + src/proguard/obfuscate/MemberNameCleaner.java | 60 + .../obfuscate/MemberNameCollector.java | 106 + .../obfuscate/MemberNameConflictFixer.java | 159 + src/proguard/obfuscate/MemberNameFilter.java | 120 + src/proguard/obfuscate/MemberObfuscator.java | 230 + .../obfuscate/MemberSpecialNameFilter.java | 101 + .../obfuscate/MultiMappingProcessor.java | 96 + src/proguard/obfuscate/NameFactory.java | 34 + .../obfuscate/NameFactoryResetter.java | 59 + src/proguard/obfuscate/NameMarker.java | 166 + .../obfuscate/NumericNameFactory.java | 49 + src/proguard/obfuscate/Obfuscator.java | 454 ++ .../obfuscate/ParameterNameMarker.java | 128 + src/proguard/obfuscate/SimpleNameFactory.java | 156 + src/proguard/obfuscate/SourceFileRenamer.java | 84 + .../obfuscate/SpecialNameFactory.java | 83 + src/proguard/obfuscate/package.html | 3 + .../BootstrapMethodArgumentShrinker.java | 103 + src/proguard/optimize/ChangedCodePrinter.java | 297 + .../optimize/ConstantMemberFilter.java | 77 + .../optimize/ConstantParameterFilter.java | 79 + .../optimize/DuplicateInitializerFixer.java | 215 + .../DuplicateInitializerInvocationFixer.java | 161 + src/proguard/optimize/KeepMarker.java | 103 + src/proguard/optimize/KeptClassFilter.java | 69 + src/proguard/optimize/KeptMemberFilter.java | 87 + .../optimize/MemberDescriptorSpecializer.java | 138 + .../optimize/MethodDescriptorShrinker.java | 317 + src/proguard/optimize/MethodStaticizer.java | 87 + .../OptimizationInfoMemberFilter.java | 94 + src/proguard/optimize/Optimizer.java | 963 ++++ src/proguard/optimize/ParameterShrinker.java | 146 + .../optimize/TailRecursionSimplifier.java | 356 ++ .../optimize/WriteOnlyFieldFilter.java | 65 + .../evaluation/EvaluationShrinker.java | 2222 +++++++ .../evaluation/EvaluationSimplifier.java | 988 ++++ .../optimize/evaluation/LivenessAnalyzer.java | 526 ++ .../evaluation/LoadingInvocationUnit.java | 195 + .../optimize/evaluation/PartialEvaluator.java | 1275 +++++ .../evaluation/StoringInvocationUnit.java | 207 + .../optimize/evaluation/TracedBranchUnit.java | 59 + .../evaluation/VariableOptimizer.java | 357 ++ src/proguard/optimize/evaluation/package.html | 4 + .../optimize/info/AccessMethodMarker.java | 202 + .../optimize/info/BackwardBranchMarker.java | 90 + .../optimize/info/CatchExceptionMarker.java | 69 + .../optimize/info/CaughtClassFilter.java | 63 + .../optimize/info/CaughtClassMarker.java | 64 + .../optimize/info/ClassOptimizationInfo.java | 165 + .../info/ClassOptimizationInfoSetter.java | 47 + .../optimize/info/DotClassFilter.java | 63 + .../optimize/info/DotClassMarker.java | 96 + .../info/ExceptionInstructionChecker.java | 193 + .../optimize/info/FieldOptimizationInfo.java | 204 + .../optimize/info/InstanceofClassFilter.java | 63 + .../optimize/info/InstanceofClassMarker.java | 93 + .../info/InstantiationClassFilter.java | 62 + .../info/InstantiationClassMarker.java | 93 + .../info/MemberOptimizationInfoSetter.java | 59 + .../optimize/info/MethodInvocationMarker.java | 107 + .../optimize/info/MethodOptimizationInfo.java | 302 + .../info/NoSideEffectMethodMarker.java | 91 + .../optimize/info/NonPrivateMemberMarker.java | 171 + ...ageVisibleMemberContainingClassMarker.java | 85 + ...ckageVisibleMemberInvokingClassMarker.java | 129 + .../optimize/info/ParameterUsageMarker.java | 285 + .../optimize/info/ReadWriteFieldMarker.java | 163 + .../info/SideEffectInstructionChecker.java | 307 + .../optimize/info/SideEffectMethodFilter.java | 73 + .../optimize/info/SideEffectMethodMarker.java | 181 + ...taticInitializerContainingClassFilter.java | 62 + ...taticInitializerContainingClassMarker.java | 65 + .../optimize/info/SuperInvocationMarker.java | 93 + .../optimize/info/VariableUsageMarker.java | 96 + src/proguard/optimize/info/package.html | 4 + src/proguard/optimize/package.html | 4 + .../optimize/peephole/BranchTargetFinder.java | 748 +++ .../optimize/peephole/ClassFinalizer.java | 84 + .../optimize/peephole/ClassMerger.java | 641 +++ .../peephole/GotoCommonCodeReplacer.java | 263 + .../optimize/peephole/GotoGotoReplacer.java | 115 + .../optimize/peephole/GotoReturnReplacer.java | 115 + .../peephole/HorizontalClassMerger.java | 90 + .../InstructionSequenceConstants.java | 5090 +++++++++++++++++ .../peephole/InstructionSequenceReplacer.java | 420 ++ .../InstructionSequencesReplacer.java | 138 + .../optimize/peephole/MemberPrivatizer.java | 103 + .../optimize/peephole/MethodFinalizer.java | 93 + .../optimize/peephole/MethodInliner.java | 599 ++ .../optimize/peephole/NopRemover.java | 89 + .../optimize/peephole/PeepholeOptimizer.java | 103 + .../peephole/ReachableCodeMarker.java | 262 + .../RetargetedInnerClassAttributeRemover.java | 170 + .../optimize/peephole/TargetClassChanger.java | 455 ++ .../peephole/UnreachableCodeRemover.java | 143 + .../peephole/UnreachableExceptionRemover.java | 163 + .../optimize/peephole/VariableShrinker.java | 129 + .../peephole/VerticalClassMerger.java | 88 + src/proguard/optimize/peephole/package.html | 3 + src/proguard/package.html | 5 + src/proguard/preverify/CodePreverifier.java | 623 ++ .../preverify/CodeSubroutineInliner.java | 389 ++ src/proguard/preverify/Preverifier.java | 73 + src/proguard/preverify/SubroutineInliner.java | 73 + src/proguard/retrace/MANIFEST.MF | 3 + src/proguard/retrace/ReTrace.java | 749 +++ src/proguard/retrace/package.html | 4 + .../shrink/AnnotationUsageMarker.java | 327 ++ src/proguard/shrink/ClassShrinker.java | 479 ++ src/proguard/shrink/InnerUsageMarker.java | 174 + src/proguard/shrink/InterfaceUsageMarker.java | 152 + .../shrink/LocalVariableTypeUsageMarker.java | 178 + src/proguard/shrink/ShortestUsageMark.java | 183 + src/proguard/shrink/ShortestUsageMarker.java | 277 + src/proguard/shrink/ShortestUsagePrinter.java | 220 + src/proguard/shrink/Shrinker.java | 179 + src/proguard/shrink/SignatureUsageMarker.java | 117 + src/proguard/shrink/UsageMarker.java | 1052 ++++ src/proguard/shrink/UsagePrinter.java | 185 + src/proguard/shrink/UsedClassFilter.java | 74 + src/proguard/shrink/UsedMemberFilter.java | 93 + src/proguard/shrink/package.html | 3 + src/proguard/util/AndMatcher.java | 49 + src/proguard/util/ArrayUtil.java | 960 ++++ src/proguard/util/ClassNameParser.java | 216 + src/proguard/util/ConstantMatcher.java | 48 + src/proguard/util/EmptyStringMatcher.java | 36 + src/proguard/util/ExtensionMatcher.java | 63 + src/proguard/util/FileNameParser.java | 121 + src/proguard/util/FixedStringMatcher.java | 56 + src/proguard/util/ListMatcher.java | 69 + src/proguard/util/ListParser.java | 137 + src/proguard/util/ListUtil.java | 180 + src/proguard/util/NameParser.java | 106 + src/proguard/util/NotMatcher.java | 46 + src/proguard/util/ObjectUtil.java | 67 + src/proguard/util/OrMatcher.java | 49 + src/proguard/util/SettableMatcher.java | 46 + src/proguard/util/StringMatcher.java | 38 + src/proguard/util/StringParser.java | 35 + src/proguard/util/VariableStringMatcher.java | 126 + src/proguard/util/package.html | 3 + src/proguard/wtk/ProGuardObfuscator.java | 142 + src/proguard/wtk/default.pro | 114 + src/proguard/wtk/package.html | 3 + 758 files changed, 128741 insertions(+) create mode 100644 README create mode 100644 bin/proguard.bat create mode 100755 bin/proguard.sh create mode 100644 bin/proguardgui.bat create mode 100755 bin/proguardgui.sh create mode 100644 bin/retrace.bat create mode 100755 bin/retrace.sh create mode 100644 build/README create mode 100644 build/build.properties create mode 100755 build/build.sh create mode 100644 build/build.xml create mode 100644 build/makefile create mode 100644 build/maven/ant/pom.xml create mode 100644 build/maven/base/pom.xml create mode 100644 build/maven/gradle/pom.xml create mode 100644 build/maven/gui/pom.xml create mode 100644 build/maven/pom.xml create mode 100644 build/maven/retrace/pom.xml create mode 100644 build/maven/wtk/pom.xml create mode 100644 docs/FAQ.html create mode 100644 docs/GPL.html create mode 100644 docs/GPL_exception.html create mode 100644 docs/acknowledgements.html create mode 100644 docs/alternatives.html create mode 100644 docs/android_shades.png create mode 100644 docs/checkmark.gif create mode 100644 docs/dexguard.png create mode 100644 docs/downloads.html create mode 100644 docs/drop1.gif create mode 100644 docs/drop2.gif create mode 100644 docs/drop3.gif create mode 100644 docs/favicon.ico create mode 100644 docs/feedback.html create mode 100644 docs/index.html create mode 100644 docs/license.html create mode 100644 docs/main.html create mode 100644 docs/manual/ant.html create mode 100644 docs/manual/attention.gif create mode 100644 docs/manual/examples.html create mode 100644 docs/manual/gradle.html create mode 100644 docs/manual/gui.html create mode 100644 docs/manual/index.html create mode 100644 docs/manual/introduction.html create mode 100644 docs/manual/limitations.html create mode 100644 docs/manual/optimizations.html create mode 100644 docs/manual/refcard.html create mode 100644 docs/manual/retrace/examples.html create mode 100644 docs/manual/retrace/index.html create mode 100644 docs/manual/retrace/introduction.html create mode 100644 docs/manual/retrace/usage.html create mode 100644 docs/manual/sections.html create mode 100644 docs/manual/style.css create mode 100644 docs/manual/troubleshooting.html create mode 100644 docs/manual/usage.html create mode 100644 docs/manual/wtk.html create mode 100644 docs/quality.html create mode 100644 docs/results.html create mode 100644 docs/saikoalogo.png create mode 100644 docs/screenshot_console.gif create mode 100644 docs/screenshot_console_small.gif create mode 100644 docs/screenshot_gui1.gif create mode 100644 docs/screenshot_gui2.gif create mode 100644 docs/screenshot_gui3.gif create mode 100644 docs/screenshot_gui4.gif create mode 100644 docs/screenshot_gui5.gif create mode 100644 docs/screenshot_gui6.gif create mode 100644 docs/screenshot_gui7.gif create mode 100644 docs/screenshot_gui8.gif create mode 100644 docs/screenshots.html create mode 100644 docs/screenshots_gui_small.gif create mode 100644 docs/sections.html create mode 100644 docs/sflogo.png create mode 100644 docs/steel.gif create mode 100644 docs/style.css create mode 100644 docs/testimonials.html create mode 100644 docs/title.gif create mode 100644 docs/title.html create mode 100644 examples/android.pro create mode 100644 examples/annotations/examples.pro create mode 100644 examples/annotations/examples/Applet.java create mode 100644 examples/annotations/examples/Application.java create mode 100644 examples/annotations/examples/Bean.java create mode 100644 examples/annotations/examples/NativeCallBack.java create mode 100644 examples/annotations/lib/annotations.jar create mode 100644 examples/annotations/lib/annotations.pro create mode 100644 examples/annotations/src/proguard/annotation/Keep.java create mode 100644 examples/annotations/src/proguard/annotation/KeepApplication.java create mode 100644 examples/annotations/src/proguard/annotation/KeepClassMemberNames.java create mode 100644 examples/annotations/src/proguard/annotation/KeepClassMembers.java create mode 100644 examples/annotations/src/proguard/annotation/KeepGettersSetters.java create mode 100644 examples/annotations/src/proguard/annotation/KeepImplementations.java create mode 100644 examples/annotations/src/proguard/annotation/KeepName.java create mode 100644 examples/annotations/src/proguard/annotation/KeepPublicClassMemberNames.java create mode 100644 examples/annotations/src/proguard/annotation/KeepPublicClassMembers.java create mode 100644 examples/annotations/src/proguard/annotation/KeepPublicGettersSetters.java create mode 100644 examples/annotations/src/proguard/annotation/KeepPublicImplementations.java create mode 100644 examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java create mode 100644 examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMembers.java create mode 100644 examples/ant/applets.xml create mode 100644 examples/ant/applications1.xml create mode 100644 examples/ant/applications2.xml create mode 100644 examples/ant/applications3.xml create mode 100644 examples/ant/library.xml create mode 100644 examples/ant/midlets.xml create mode 100644 examples/ant/proguard.xml create mode 100644 examples/ant/servlets.xml create mode 100644 examples/applets.pro create mode 100644 examples/applications.pro create mode 100644 examples/dictionaries/compact.txt create mode 100644 examples/dictionaries/keywords.txt create mode 100644 examples/dictionaries/shakespeare.txt create mode 100644 examples/dictionaries/windows.txt create mode 100644 examples/gradle/android.gradle create mode 100644 examples/gradle/applets.gradle create mode 100644 examples/gradle/applications.gradle create mode 100644 examples/gradle/library.gradle create mode 100644 examples/gradle/midlets.gradle create mode 100644 examples/gradle/proguard.gradle create mode 100644 examples/gradle/proguardall.gradle create mode 100644 examples/gradle/proguardgui.gradle create mode 100644 examples/gradle/retrace.gradle create mode 100644 examples/gradle/scala.gradle create mode 100644 examples/gradle/servlets.gradle create mode 100644 examples/library.pro create mode 100644 examples/midlets.pro create mode 100644 examples/proguard.pro create mode 100644 examples/proguardall.pro create mode 100644 examples/proguardgui.pro create mode 100644 examples/retrace.pro create mode 100644 examples/scala.pro create mode 100644 examples/servlets.pro create mode 100644 src/proguard/ArgumentWordReader.java create mode 100644 src/proguard/ClassPath.java create mode 100644 src/proguard/ClassPathEntry.java create mode 100644 src/proguard/ClassSpecification.java create mode 100644 src/proguard/ClassSpecificationVisitorFactory.java create mode 100644 src/proguard/Configuration.java create mode 100644 src/proguard/ConfigurationConstants.java create mode 100644 src/proguard/ConfigurationParser.java create mode 100644 src/proguard/ConfigurationWriter.java create mode 100644 src/proguard/DataEntryReaderFactory.java create mode 100644 src/proguard/DataEntryWriterFactory.java create mode 100644 src/proguard/DescriptorKeepChecker.java create mode 100644 src/proguard/DuplicateClassPrinter.java create mode 100644 src/proguard/FileWordReader.java create mode 100644 src/proguard/FullyQualifiedClassNameChecker.java create mode 100644 src/proguard/GPL.java create mode 100644 src/proguard/Initializer.java create mode 100644 src/proguard/InputReader.java create mode 100644 src/proguard/KeepClassMemberChecker.java create mode 100644 src/proguard/KeepClassSpecification.java create mode 100644 src/proguard/LineWordReader.java create mode 100644 src/proguard/MANIFEST.MF create mode 100644 src/proguard/MemberSpecification.java create mode 100644 src/proguard/OutputWriter.java create mode 100644 src/proguard/ParseException.java create mode 100644 src/proguard/ProGuard.java create mode 100644 src/proguard/SeedPrinter.java create mode 100644 src/proguard/SubclassedClassFilter.java create mode 100644 src/proguard/Targeter.java create mode 100644 src/proguard/UpToDateChecker.java create mode 100644 src/proguard/WordReader.java create mode 100644 src/proguard/ant/ClassPathElement.java create mode 100644 src/proguard/ant/ClassSpecificationElement.java create mode 100644 src/proguard/ant/ConfigurationElement.java create mode 100644 src/proguard/ant/ConfigurationTask.java create mode 100644 src/proguard/ant/FilterElement.java create mode 100644 src/proguard/ant/KeepSpecificationElement.java create mode 100644 src/proguard/ant/MemberSpecificationElement.java create mode 100644 src/proguard/ant/ProGuardTask.java create mode 100644 src/proguard/ant/package.html create mode 100644 src/proguard/ant/task.properties create mode 100644 src/proguard/classfile/ClassConstants.java create mode 100644 src/proguard/classfile/ClassPool.java create mode 100644 src/proguard/classfile/Clazz.java create mode 100644 src/proguard/classfile/Field.java create mode 100644 src/proguard/classfile/LibraryClass.java create mode 100644 src/proguard/classfile/LibraryField.java create mode 100644 src/proguard/classfile/LibraryMember.java create mode 100644 src/proguard/classfile/LibraryMethod.java create mode 100644 src/proguard/classfile/Member.java create mode 100644 src/proguard/classfile/Method.java create mode 100644 src/proguard/classfile/ProgramClass.java create mode 100644 src/proguard/classfile/ProgramField.java create mode 100644 src/proguard/classfile/ProgramMember.java create mode 100644 src/proguard/classfile/ProgramMethod.java create mode 100644 src/proguard/classfile/VisitorAccepter.java create mode 100644 src/proguard/classfile/attribute/Attribute.java create mode 100755 src/proguard/classfile/attribute/BootstrapMethodInfo.java create mode 100755 src/proguard/classfile/attribute/BootstrapMethodsAttribute.java create mode 100644 src/proguard/classfile/attribute/CodeAttribute.java create mode 100644 src/proguard/classfile/attribute/ConstantValueAttribute.java create mode 100644 src/proguard/classfile/attribute/DeprecatedAttribute.java create mode 100644 src/proguard/classfile/attribute/EnclosingMethodAttribute.java create mode 100644 src/proguard/classfile/attribute/ExceptionInfo.java create mode 100644 src/proguard/classfile/attribute/ExceptionsAttribute.java create mode 100644 src/proguard/classfile/attribute/InnerClassesAttribute.java create mode 100644 src/proguard/classfile/attribute/InnerClassesInfo.java create mode 100644 src/proguard/classfile/attribute/LineNumberInfo.java create mode 100644 src/proguard/classfile/attribute/LineNumberTableAttribute.java create mode 100644 src/proguard/classfile/attribute/LocalVariableInfo.java create mode 100644 src/proguard/classfile/attribute/LocalVariableTableAttribute.java create mode 100644 src/proguard/classfile/attribute/LocalVariableTypeInfo.java create mode 100644 src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java create mode 100644 src/proguard/classfile/attribute/SignatureAttribute.java create mode 100644 src/proguard/classfile/attribute/SourceDirAttribute.java create mode 100644 src/proguard/classfile/attribute/SourceFileAttribute.java create mode 100644 src/proguard/classfile/attribute/SyntheticAttribute.java create mode 100644 src/proguard/classfile/attribute/UnknownAttribute.java create mode 100644 src/proguard/classfile/attribute/annotation/Annotation.java create mode 100644 src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java create mode 100644 src/proguard/classfile/attribute/annotation/AnnotationElementValue.java create mode 100644 src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java create mode 100644 src/proguard/classfile/attribute/annotation/ArrayElementValue.java create mode 100644 src/proguard/classfile/attribute/annotation/ClassElementValue.java create mode 100644 src/proguard/classfile/attribute/annotation/ConstantElementValue.java create mode 100644 src/proguard/classfile/attribute/annotation/ElementValue.java create mode 100644 src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java create mode 100644 src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java create mode 100644 src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java create mode 100644 src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java create mode 100644 src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java create mode 100644 src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java create mode 100644 src/proguard/classfile/attribute/annotation/package.html create mode 100644 src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java create mode 100644 src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java create mode 100644 src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java create mode 100644 src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java create mode 100644 src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java create mode 100644 src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java create mode 100644 src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java create mode 100644 src/proguard/classfile/attribute/annotation/visitor/package.html create mode 100644 src/proguard/classfile/attribute/package.html create mode 100644 src/proguard/classfile/attribute/preverification/DoubleType.java create mode 100644 src/proguard/classfile/attribute/preverification/FloatType.java create mode 100644 src/proguard/classfile/attribute/preverification/FullFrame.java create mode 100644 src/proguard/classfile/attribute/preverification/IntegerType.java create mode 100644 src/proguard/classfile/attribute/preverification/LessZeroFrame.java create mode 100644 src/proguard/classfile/attribute/preverification/LongType.java create mode 100644 src/proguard/classfile/attribute/preverification/MoreZeroFrame.java create mode 100644 src/proguard/classfile/attribute/preverification/NullType.java create mode 100644 src/proguard/classfile/attribute/preverification/ObjectType.java create mode 100644 src/proguard/classfile/attribute/preverification/SameOneFrame.java create mode 100644 src/proguard/classfile/attribute/preverification/SameZeroFrame.java create mode 100644 src/proguard/classfile/attribute/preverification/StackMapAttribute.java create mode 100644 src/proguard/classfile/attribute/preverification/StackMapFrame.java create mode 100644 src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java create mode 100644 src/proguard/classfile/attribute/preverification/TopType.java create mode 100644 src/proguard/classfile/attribute/preverification/UninitializedThisType.java create mode 100644 src/proguard/classfile/attribute/preverification/UninitializedType.java create mode 100644 src/proguard/classfile/attribute/preverification/VerificationType.java create mode 100644 src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java create mode 100644 src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java create mode 100644 src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/AttributeNameFilter.java create mode 100644 src/proguard/classfile/attribute/visitor/AttributeVisitor.java create mode 100755 src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java create mode 100644 src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java create mode 100644 src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java create mode 100644 src/proguard/classfile/attribute/visitor/StackSizeComputer.java create mode 100644 src/proguard/classfile/attribute/visitor/package.html create mode 100644 src/proguard/classfile/constant/ClassConstant.java create mode 100644 src/proguard/classfile/constant/Constant.java create mode 100644 src/proguard/classfile/constant/DoubleConstant.java create mode 100644 src/proguard/classfile/constant/FieldrefConstant.java create mode 100644 src/proguard/classfile/constant/FloatConstant.java create mode 100644 src/proguard/classfile/constant/IntegerConstant.java create mode 100644 src/proguard/classfile/constant/InterfaceMethodrefConstant.java create mode 100755 src/proguard/classfile/constant/InvokeDynamicConstant.java create mode 100644 src/proguard/classfile/constant/LongConstant.java create mode 100755 src/proguard/classfile/constant/MethodHandleConstant.java create mode 100644 src/proguard/classfile/constant/MethodTypeConstant.java create mode 100644 src/proguard/classfile/constant/MethodrefConstant.java create mode 100644 src/proguard/classfile/constant/NameAndTypeConstant.java create mode 100644 src/proguard/classfile/constant/RefConstant.java create mode 100644 src/proguard/classfile/constant/StringConstant.java create mode 100644 src/proguard/classfile/constant/Utf8Constant.java create mode 100644 src/proguard/classfile/constant/visitor/AllConstantVisitor.java create mode 100644 src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java create mode 100644 src/proguard/classfile/constant/visitor/ConstantTagFilter.java create mode 100644 src/proguard/classfile/constant/visitor/ConstantVisitor.java create mode 100644 src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java create mode 100644 src/proguard/classfile/constant/visitor/MethodrefTraveler.java create mode 100644 src/proguard/classfile/constant/visitor/package.html create mode 100644 src/proguard/classfile/editor/AccessFixer.java create mode 100644 src/proguard/classfile/editor/AnnotationAdder.java create mode 100644 src/proguard/classfile/editor/AnnotationsAttributeEditor.java create mode 100644 src/proguard/classfile/editor/AttributeAdder.java create mode 100644 src/proguard/classfile/editor/AttributeSorter.java create mode 100644 src/proguard/classfile/editor/AttributesEditor.java create mode 100644 src/proguard/classfile/editor/BridgeMethodFixer.java create mode 100644 src/proguard/classfile/editor/ClassEditor.java create mode 100644 src/proguard/classfile/editor/ClassElementSorter.java create mode 100644 src/proguard/classfile/editor/ClassMemberSorter.java create mode 100644 src/proguard/classfile/editor/ClassReferenceFixer.java create mode 100644 src/proguard/classfile/editor/CodeAttributeComposer.java create mode 100644 src/proguard/classfile/editor/CodeAttributeEditor.java create mode 100644 src/proguard/classfile/editor/CodeAttributeEditorResetter.java create mode 100644 src/proguard/classfile/editor/ComparableConstant.java create mode 100644 src/proguard/classfile/editor/ConstantAdder.java create mode 100644 src/proguard/classfile/editor/ConstantPoolEditor.java create mode 100644 src/proguard/classfile/editor/ConstantPoolRemapper.java create mode 100644 src/proguard/classfile/editor/ConstantPoolShrinker.java create mode 100644 src/proguard/classfile/editor/ConstantPoolSorter.java create mode 100644 src/proguard/classfile/editor/ElementValueAdder.java create mode 100644 src/proguard/classfile/editor/ElementValuesEditor.java create mode 100644 src/proguard/classfile/editor/ExceptionAdder.java create mode 100644 src/proguard/classfile/editor/ExceptionInfoAdder.java create mode 100644 src/proguard/classfile/editor/ExceptionsAttributeEditor.java create mode 100644 src/proguard/classfile/editor/InnerClassesAccessFixer.java create mode 100644 src/proguard/classfile/editor/InstructionAdder.java create mode 100644 src/proguard/classfile/editor/InstructionWriter.java create mode 100644 src/proguard/classfile/editor/InterfaceAdder.java create mode 100644 src/proguard/classfile/editor/InterfaceSorter.java create mode 100644 src/proguard/classfile/editor/InterfacesEditor.java create mode 100644 src/proguard/classfile/editor/LineNumberInfoAdder.java create mode 100644 src/proguard/classfile/editor/LineNumberTableAttributeEditor.java create mode 100644 src/proguard/classfile/editor/LocalVariableInfoAdder.java create mode 100644 src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java create mode 100644 src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java create mode 100644 src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java create mode 100644 src/proguard/classfile/editor/MemberAdder.java create mode 100644 src/proguard/classfile/editor/MemberReferenceFixer.java create mode 100644 src/proguard/classfile/editor/MethodInvocationFixer.java create mode 100644 src/proguard/classfile/editor/NameAndTypeShrinker.java create mode 100644 src/proguard/classfile/editor/NamedAttributeDeleter.java create mode 100644 src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java create mode 100644 src/proguard/classfile/editor/StackSizeUpdater.java create mode 100644 src/proguard/classfile/editor/SubclassAdder.java create mode 100644 src/proguard/classfile/editor/SubclassToAdder.java create mode 100644 src/proguard/classfile/editor/Utf8Shrinker.java create mode 100644 src/proguard/classfile/editor/VariableCleaner.java create mode 100644 src/proguard/classfile/editor/VariableEditor.java create mode 100644 src/proguard/classfile/editor/VariableRemapper.java create mode 100644 src/proguard/classfile/editor/VariableSizeUpdater.java create mode 100644 src/proguard/classfile/editor/package.html create mode 100644 src/proguard/classfile/instruction/BranchInstruction.java create mode 100644 src/proguard/classfile/instruction/ConstantInstruction.java create mode 100644 src/proguard/classfile/instruction/Instruction.java create mode 100644 src/proguard/classfile/instruction/InstructionConstants.java create mode 100644 src/proguard/classfile/instruction/InstructionFactory.java create mode 100644 src/proguard/classfile/instruction/InstructionUtil.java create mode 100644 src/proguard/classfile/instruction/LookUpSwitchInstruction.java create mode 100644 src/proguard/classfile/instruction/SimpleInstruction.java create mode 100644 src/proguard/classfile/instruction/SwitchInstruction.java create mode 100644 src/proguard/classfile/instruction/TableSwitchInstruction.java create mode 100644 src/proguard/classfile/instruction/VariableInstruction.java create mode 100644 src/proguard/classfile/instruction/package.html create mode 100644 src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java create mode 100644 src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java create mode 100644 src/proguard/classfile/instruction/visitor/InstructionCounter.java create mode 100644 src/proguard/classfile/instruction/visitor/InstructionVisitor.java create mode 100644 src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java create mode 100644 src/proguard/classfile/instruction/visitor/package.html create mode 100644 src/proguard/classfile/io/LibraryClassReader.java create mode 100644 src/proguard/classfile/io/ProgramClassReader.java create mode 100644 src/proguard/classfile/io/ProgramClassWriter.java create mode 100644 src/proguard/classfile/io/RuntimeDataInput.java create mode 100644 src/proguard/classfile/io/RuntimeDataOutput.java create mode 100644 src/proguard/classfile/io/package.html create mode 100644 src/proguard/classfile/package.html create mode 100644 src/proguard/classfile/util/AccessUtil.java create mode 100644 src/proguard/classfile/util/ClassReferenceInitializer.java create mode 100644 src/proguard/classfile/util/ClassSubHierarchyInitializer.java create mode 100644 src/proguard/classfile/util/ClassSuperHierarchyInitializer.java create mode 100644 src/proguard/classfile/util/ClassUtil.java create mode 100644 src/proguard/classfile/util/DescriptorClassEnumeration.java create mode 100644 src/proguard/classfile/util/DynamicClassReferenceInitializer.java create mode 100644 src/proguard/classfile/util/DynamicMemberReferenceInitializer.java create mode 100644 src/proguard/classfile/util/EnumFieldReferenceInitializer.java create mode 100644 src/proguard/classfile/util/ExternalTypeEnumeration.java create mode 100644 src/proguard/classfile/util/InstructionSequenceMatcher.java create mode 100644 src/proguard/classfile/util/InternalTypeEnumeration.java create mode 100644 src/proguard/classfile/util/MemberFinder.java create mode 100644 src/proguard/classfile/util/MethodLinker.java create mode 100644 src/proguard/classfile/util/SimplifiedVisitor.java create mode 100644 src/proguard/classfile/util/StringReferenceInitializer.java create mode 100644 src/proguard/classfile/util/StringSharer.java create mode 100644 src/proguard/classfile/util/WarningPrinter.java create mode 100644 src/proguard/classfile/util/package.html create mode 100644 src/proguard/classfile/visitor/AllClassVisitor.java create mode 100644 src/proguard/classfile/visitor/AllFieldVisitor.java create mode 100644 src/proguard/classfile/visitor/AllMemberVisitor.java create mode 100644 src/proguard/classfile/visitor/AllMethodVisitor.java create mode 100644 src/proguard/classfile/visitor/BottomClassFilter.java create mode 100644 src/proguard/classfile/visitor/ClassAccessFilter.java create mode 100644 src/proguard/classfile/visitor/ClassCleaner.java create mode 100644 src/proguard/classfile/visitor/ClassCollector.java create mode 100644 src/proguard/classfile/visitor/ClassCounter.java create mode 100644 src/proguard/classfile/visitor/ClassHierarchyTraveler.java create mode 100644 src/proguard/classfile/visitor/ClassNameFilter.java create mode 100644 src/proguard/classfile/visitor/ClassPoolFiller.java create mode 100644 src/proguard/classfile/visitor/ClassPoolVisitor.java create mode 100644 src/proguard/classfile/visitor/ClassPresenceFilter.java create mode 100644 src/proguard/classfile/visitor/ClassPrinter.java create mode 100644 src/proguard/classfile/visitor/ClassVersionFilter.java create mode 100644 src/proguard/classfile/visitor/ClassVersionSetter.java create mode 100644 src/proguard/classfile/visitor/ClassVisitor.java create mode 100644 src/proguard/classfile/visitor/ConcreteClassDownTraveler.java create mode 100644 src/proguard/classfile/visitor/DotClassClassVisitor.java create mode 100644 src/proguard/classfile/visitor/ExceptClassFilter.java create mode 100644 src/proguard/classfile/visitor/ExceptClassesFilter.java create mode 100644 src/proguard/classfile/visitor/ExceptionCounter.java create mode 100644 src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java create mode 100644 src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java create mode 100644 src/proguard/classfile/visitor/ExceptionHandlerFilter.java create mode 100644 src/proguard/classfile/visitor/ExceptionOffsetFilter.java create mode 100644 src/proguard/classfile/visitor/ExceptionRangeFilter.java create mode 100644 src/proguard/classfile/visitor/ImplementedClassConstantFilter.java create mode 100644 src/proguard/classfile/visitor/ImplementedClassFilter.java create mode 100644 src/proguard/classfile/visitor/ImplementingClassConstantFilter.java create mode 100644 src/proguard/classfile/visitor/LibraryClassFilter.java create mode 100644 src/proguard/classfile/visitor/LibraryMemberFilter.java create mode 100644 src/proguard/classfile/visitor/MemberAccessFilter.java create mode 100644 src/proguard/classfile/visitor/MemberClassAccessFilter.java create mode 100644 src/proguard/classfile/visitor/MemberCollector.java create mode 100644 src/proguard/classfile/visitor/MemberCounter.java create mode 100644 src/proguard/classfile/visitor/MemberDescriptorFilter.java create mode 100644 src/proguard/classfile/visitor/MemberNameFilter.java create mode 100644 src/proguard/classfile/visitor/MemberToClassVisitor.java create mode 100644 src/proguard/classfile/visitor/MemberVisitor.java create mode 100644 src/proguard/classfile/visitor/MethodImplementationFilter.java create mode 100644 src/proguard/classfile/visitor/MethodImplementationTraveler.java create mode 100644 src/proguard/classfile/visitor/MultiClassPoolVisitor.java create mode 100644 src/proguard/classfile/visitor/MultiClassVisitor.java create mode 100644 src/proguard/classfile/visitor/MultiMemberVisitor.java create mode 100644 src/proguard/classfile/visitor/NamedClassVisitor.java create mode 100644 src/proguard/classfile/visitor/NamedFieldVisitor.java create mode 100644 src/proguard/classfile/visitor/NamedMethodVisitor.java create mode 100644 src/proguard/classfile/visitor/ProgramClassFilter.java create mode 100644 src/proguard/classfile/visitor/ProgramMemberFilter.java create mode 100644 src/proguard/classfile/visitor/ReferencedClassVisitor.java create mode 100644 src/proguard/classfile/visitor/ReferencedMemberVisitor.java create mode 100644 src/proguard/classfile/visitor/SimilarMemberVisitor.java create mode 100644 src/proguard/classfile/visitor/SimpleClassPrinter.java create mode 100644 src/proguard/classfile/visitor/SubclassFilter.java create mode 100644 src/proguard/classfile/visitor/SubclassTraveler.java create mode 100644 src/proguard/classfile/visitor/VariableClassVisitor.java create mode 100644 src/proguard/classfile/visitor/VariableMemberVisitor.java create mode 100644 src/proguard/classfile/visitor/package.html create mode 100644 src/proguard/evaluation/BasicBranchUnit.java create mode 100644 src/proguard/evaluation/BasicInvocationUnit.java create mode 100644 src/proguard/evaluation/BranchUnit.java create mode 100644 src/proguard/evaluation/ClassConstantValueFactory.java create mode 100644 src/proguard/evaluation/ConstantValueFactory.java create mode 100644 src/proguard/evaluation/InvocationUnit.java create mode 100644 src/proguard/evaluation/Processor.java create mode 100644 src/proguard/evaluation/Stack.java create mode 100644 src/proguard/evaluation/TracedStack.java create mode 100644 src/proguard/evaluation/TracedVariables.java create mode 100644 src/proguard/evaluation/Variables.java create mode 100644 src/proguard/evaluation/value/Category1Value.java create mode 100644 src/proguard/evaluation/value/Category2Value.java create mode 100644 src/proguard/evaluation/value/ComparisonValue.java create mode 100644 src/proguard/evaluation/value/CompositeDoubleValue.java create mode 100644 src/proguard/evaluation/value/CompositeFloatValue.java create mode 100644 src/proguard/evaluation/value/CompositeIntegerValue.java create mode 100644 src/proguard/evaluation/value/CompositeLongValue.java create mode 100644 src/proguard/evaluation/value/ConvertedByteValue.java create mode 100644 src/proguard/evaluation/value/ConvertedCharacterValue.java create mode 100644 src/proguard/evaluation/value/ConvertedDoubleValue.java create mode 100644 src/proguard/evaluation/value/ConvertedFloatValue.java create mode 100644 src/proguard/evaluation/value/ConvertedIntegerValue.java create mode 100644 src/proguard/evaluation/value/ConvertedLongValue.java create mode 100644 src/proguard/evaluation/value/ConvertedShortValue.java create mode 100644 src/proguard/evaluation/value/DoubleValue.java create mode 100644 src/proguard/evaluation/value/FloatValue.java create mode 100644 src/proguard/evaluation/value/IdentifiedDoubleValue.java create mode 100644 src/proguard/evaluation/value/IdentifiedFloatValue.java create mode 100644 src/proguard/evaluation/value/IdentifiedIntegerValue.java create mode 100644 src/proguard/evaluation/value/IdentifiedLongValue.java create mode 100644 src/proguard/evaluation/value/IdentifiedReferenceValue.java create mode 100644 src/proguard/evaluation/value/IdentifiedValueFactory.java create mode 100644 src/proguard/evaluation/value/InstructionOffsetValue.java create mode 100644 src/proguard/evaluation/value/IntegerValue.java create mode 100644 src/proguard/evaluation/value/LongValue.java create mode 100644 src/proguard/evaluation/value/NegatedDoubleValue.java create mode 100644 src/proguard/evaluation/value/NegatedFloatValue.java create mode 100644 src/proguard/evaluation/value/NegatedIntegerValue.java create mode 100644 src/proguard/evaluation/value/NegatedLongValue.java create mode 100644 src/proguard/evaluation/value/ParticularDoubleValue.java create mode 100644 src/proguard/evaluation/value/ParticularFloatValue.java create mode 100644 src/proguard/evaluation/value/ParticularIntegerValue.java create mode 100644 src/proguard/evaluation/value/ParticularLongValue.java create mode 100644 src/proguard/evaluation/value/ReferenceValue.java create mode 100644 src/proguard/evaluation/value/SpecificDoubleValue.java create mode 100644 src/proguard/evaluation/value/SpecificFloatValue.java create mode 100644 src/proguard/evaluation/value/SpecificIntegerValue.java create mode 100644 src/proguard/evaluation/value/SpecificLongValue.java create mode 100644 src/proguard/evaluation/value/SpecificValueFactory.java create mode 100644 src/proguard/evaluation/value/TopValue.java create mode 100644 src/proguard/evaluation/value/UnknownDoubleValue.java create mode 100644 src/proguard/evaluation/value/UnknownFloatValue.java create mode 100644 src/proguard/evaluation/value/UnknownIntegerValue.java create mode 100644 src/proguard/evaluation/value/UnknownLongValue.java create mode 100644 src/proguard/evaluation/value/Value.java create mode 100644 src/proguard/evaluation/value/ValueFactory.java create mode 100644 src/proguard/evaluation/value/package.html create mode 100644 src/proguard/gradle/ProGuardTask.java create mode 100644 src/proguard/gui/ClassPathPanel.java create mode 100644 src/proguard/gui/ClassSpecificationDialog.java create mode 100644 src/proguard/gui/ClassSpecificationsPanel.java create mode 100644 src/proguard/gui/ExtensionFileFilter.java create mode 100644 src/proguard/gui/FilterBuilder.java create mode 100644 src/proguard/gui/FilterDialog.java create mode 100644 src/proguard/gui/GUIResources.java create mode 100644 src/proguard/gui/GUIResources.properties create mode 100644 src/proguard/gui/KeepSpecificationsPanel.java create mode 100644 src/proguard/gui/ListPanel.java create mode 100644 src/proguard/gui/MANIFEST.MF create mode 100644 src/proguard/gui/MemberSpecificationDialog.java create mode 100644 src/proguard/gui/MemberSpecificationsPanel.java create mode 100644 src/proguard/gui/MessageDialogRunnable.java create mode 100644 src/proguard/gui/OptimizationsDialog.java create mode 100644 src/proguard/gui/ProGuardGUI.java create mode 100644 src/proguard/gui/ProGuardRunnable.java create mode 100644 src/proguard/gui/ReTraceRunnable.java create mode 100644 src/proguard/gui/SwingUtil.java create mode 100644 src/proguard/gui/TabbedPane.java create mode 100644 src/proguard/gui/TextAreaOutputStream.java create mode 100644 src/proguard/gui/arrow.gif create mode 100644 src/proguard/gui/boilerplate.pro create mode 100644 src/proguard/gui/default.pro create mode 100644 src/proguard/gui/package.html create mode 100644 src/proguard/gui/splash/BufferedSprite.java create mode 100644 src/proguard/gui/splash/CircleSprite.java create mode 100644 src/proguard/gui/splash/ClipSprite.java create mode 100644 src/proguard/gui/splash/ColorSprite.java create mode 100644 src/proguard/gui/splash/CompositeSprite.java create mode 100644 src/proguard/gui/splash/ConstantColor.java create mode 100644 src/proguard/gui/splash/ConstantDouble.java create mode 100644 src/proguard/gui/splash/ConstantFont.java create mode 100644 src/proguard/gui/splash/ConstantInt.java create mode 100644 src/proguard/gui/splash/ConstantString.java create mode 100644 src/proguard/gui/splash/ConstantTiming.java create mode 100644 src/proguard/gui/splash/FontSprite.java create mode 100644 src/proguard/gui/splash/ImageSprite.java create mode 100644 src/proguard/gui/splash/LinearColor.java create mode 100644 src/proguard/gui/splash/LinearDouble.java create mode 100644 src/proguard/gui/splash/LinearInt.java create mode 100644 src/proguard/gui/splash/LinearTiming.java create mode 100644 src/proguard/gui/splash/OverrideGraphics2D.java create mode 100644 src/proguard/gui/splash/RectangleSprite.java create mode 100644 src/proguard/gui/splash/SawToothTiming.java create mode 100644 src/proguard/gui/splash/ShadowedSprite.java create mode 100644 src/proguard/gui/splash/SineTiming.java create mode 100644 src/proguard/gui/splash/SmoothTiming.java create mode 100644 src/proguard/gui/splash/SplashPanel.java create mode 100644 src/proguard/gui/splash/Sprite.java create mode 100644 src/proguard/gui/splash/TextSprite.java create mode 100644 src/proguard/gui/splash/TimeSwitchSprite.java create mode 100644 src/proguard/gui/splash/Timing.java create mode 100644 src/proguard/gui/splash/TypeWriterString.java create mode 100644 src/proguard/gui/splash/VariableColor.java create mode 100644 src/proguard/gui/splash/VariableDouble.java create mode 100644 src/proguard/gui/splash/VariableFont.java create mode 100644 src/proguard/gui/splash/VariableInt.java create mode 100644 src/proguard/gui/splash/VariableSizeFont.java create mode 100644 src/proguard/gui/splash/VariableString.java create mode 100644 src/proguard/gui/splash/package.html create mode 100644 src/proguard/gui/vtitle.png create mode 100644 src/proguard/io/CascadingDataEntryWriter.java create mode 100644 src/proguard/io/ClassFilter.java create mode 100644 src/proguard/io/ClassReader.java create mode 100644 src/proguard/io/ClassRewriter.java create mode 100644 src/proguard/io/DataEntry.java create mode 100644 src/proguard/io/DataEntryClassWriter.java create mode 100644 src/proguard/io/DataEntryCopier.java create mode 100644 src/proguard/io/DataEntryDirectoryFilter.java create mode 100644 src/proguard/io/DataEntryFilter.java create mode 100644 src/proguard/io/DataEntryNameFilter.java create mode 100644 src/proguard/io/DataEntryObfuscator.java create mode 100644 src/proguard/io/DataEntryParentFilter.java create mode 100644 src/proguard/io/DataEntryPump.java create mode 100644 src/proguard/io/DataEntryReader.java create mode 100644 src/proguard/io/DataEntryRenamer.java create mode 100644 src/proguard/io/DataEntryRewriter.java create mode 100644 src/proguard/io/DataEntryWriter.java create mode 100644 src/proguard/io/DirectoryFilter.java create mode 100644 src/proguard/io/DirectoryPump.java create mode 100644 src/proguard/io/DirectoryWriter.java create mode 100644 src/proguard/io/FileDataEntry.java create mode 100644 src/proguard/io/FilteredDataEntryReader.java create mode 100644 src/proguard/io/FilteredDataEntryWriter.java create mode 100644 src/proguard/io/Finisher.java create mode 100644 src/proguard/io/JarReader.java create mode 100644 src/proguard/io/JarWriter.java create mode 100644 src/proguard/io/ManifestRewriter.java create mode 100644 src/proguard/io/NameFilter.java create mode 100644 src/proguard/io/ParentDataEntryWriter.java create mode 100644 src/proguard/io/RenamedDataEntry.java create mode 100644 src/proguard/io/ZipDataEntry.java create mode 100644 src/proguard/io/package.html create mode 100644 src/proguard/obfuscate/AttributeShrinker.java create mode 100644 src/proguard/obfuscate/AttributeUsageMarker.java create mode 100644 src/proguard/obfuscate/ClassObfuscator.java create mode 100644 src/proguard/obfuscate/ClassRenamer.java create mode 100644 src/proguard/obfuscate/DictionaryNameFactory.java create mode 100644 src/proguard/obfuscate/MapCleaner.java create mode 100644 src/proguard/obfuscate/MappingKeeper.java create mode 100644 src/proguard/obfuscate/MappingPrinter.java create mode 100644 src/proguard/obfuscate/MappingProcessor.java create mode 100644 src/proguard/obfuscate/MappingReader.java create mode 100644 src/proguard/obfuscate/MemberNameCleaner.java create mode 100644 src/proguard/obfuscate/MemberNameCollector.java create mode 100644 src/proguard/obfuscate/MemberNameConflictFixer.java create mode 100644 src/proguard/obfuscate/MemberNameFilter.java create mode 100644 src/proguard/obfuscate/MemberObfuscator.java create mode 100644 src/proguard/obfuscate/MemberSpecialNameFilter.java create mode 100644 src/proguard/obfuscate/MultiMappingProcessor.java create mode 100644 src/proguard/obfuscate/NameFactory.java create mode 100644 src/proguard/obfuscate/NameFactoryResetter.java create mode 100644 src/proguard/obfuscate/NameMarker.java create mode 100644 src/proguard/obfuscate/NumericNameFactory.java create mode 100644 src/proguard/obfuscate/Obfuscator.java create mode 100644 src/proguard/obfuscate/ParameterNameMarker.java create mode 100644 src/proguard/obfuscate/SimpleNameFactory.java create mode 100644 src/proguard/obfuscate/SourceFileRenamer.java create mode 100644 src/proguard/obfuscate/SpecialNameFactory.java create mode 100644 src/proguard/obfuscate/package.html create mode 100644 src/proguard/optimize/BootstrapMethodArgumentShrinker.java create mode 100644 src/proguard/optimize/ChangedCodePrinter.java create mode 100644 src/proguard/optimize/ConstantMemberFilter.java create mode 100644 src/proguard/optimize/ConstantParameterFilter.java create mode 100644 src/proguard/optimize/DuplicateInitializerFixer.java create mode 100644 src/proguard/optimize/DuplicateInitializerInvocationFixer.java create mode 100644 src/proguard/optimize/KeepMarker.java create mode 100644 src/proguard/optimize/KeptClassFilter.java create mode 100644 src/proguard/optimize/KeptMemberFilter.java create mode 100644 src/proguard/optimize/MemberDescriptorSpecializer.java create mode 100644 src/proguard/optimize/MethodDescriptorShrinker.java create mode 100644 src/proguard/optimize/MethodStaticizer.java create mode 100644 src/proguard/optimize/OptimizationInfoMemberFilter.java create mode 100644 src/proguard/optimize/Optimizer.java create mode 100644 src/proguard/optimize/ParameterShrinker.java create mode 100644 src/proguard/optimize/TailRecursionSimplifier.java create mode 100644 src/proguard/optimize/WriteOnlyFieldFilter.java create mode 100644 src/proguard/optimize/evaluation/EvaluationShrinker.java create mode 100644 src/proguard/optimize/evaluation/EvaluationSimplifier.java create mode 100644 src/proguard/optimize/evaluation/LivenessAnalyzer.java create mode 100644 src/proguard/optimize/evaluation/LoadingInvocationUnit.java create mode 100644 src/proguard/optimize/evaluation/PartialEvaluator.java create mode 100644 src/proguard/optimize/evaluation/StoringInvocationUnit.java create mode 100644 src/proguard/optimize/evaluation/TracedBranchUnit.java create mode 100644 src/proguard/optimize/evaluation/VariableOptimizer.java create mode 100644 src/proguard/optimize/evaluation/package.html create mode 100644 src/proguard/optimize/info/AccessMethodMarker.java create mode 100644 src/proguard/optimize/info/BackwardBranchMarker.java create mode 100644 src/proguard/optimize/info/CatchExceptionMarker.java create mode 100644 src/proguard/optimize/info/CaughtClassFilter.java create mode 100644 src/proguard/optimize/info/CaughtClassMarker.java create mode 100644 src/proguard/optimize/info/ClassOptimizationInfo.java create mode 100644 src/proguard/optimize/info/ClassOptimizationInfoSetter.java create mode 100644 src/proguard/optimize/info/DotClassFilter.java create mode 100644 src/proguard/optimize/info/DotClassMarker.java create mode 100644 src/proguard/optimize/info/ExceptionInstructionChecker.java create mode 100644 src/proguard/optimize/info/FieldOptimizationInfo.java create mode 100644 src/proguard/optimize/info/InstanceofClassFilter.java create mode 100644 src/proguard/optimize/info/InstanceofClassMarker.java create mode 100644 src/proguard/optimize/info/InstantiationClassFilter.java create mode 100644 src/proguard/optimize/info/InstantiationClassMarker.java create mode 100644 src/proguard/optimize/info/MemberOptimizationInfoSetter.java create mode 100644 src/proguard/optimize/info/MethodInvocationMarker.java create mode 100644 src/proguard/optimize/info/MethodOptimizationInfo.java create mode 100644 src/proguard/optimize/info/NoSideEffectMethodMarker.java create mode 100644 src/proguard/optimize/info/NonPrivateMemberMarker.java create mode 100644 src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java create mode 100644 src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java create mode 100644 src/proguard/optimize/info/ParameterUsageMarker.java create mode 100644 src/proguard/optimize/info/ReadWriteFieldMarker.java create mode 100644 src/proguard/optimize/info/SideEffectInstructionChecker.java create mode 100644 src/proguard/optimize/info/SideEffectMethodFilter.java create mode 100644 src/proguard/optimize/info/SideEffectMethodMarker.java create mode 100644 src/proguard/optimize/info/StaticInitializerContainingClassFilter.java create mode 100644 src/proguard/optimize/info/StaticInitializerContainingClassMarker.java create mode 100644 src/proguard/optimize/info/SuperInvocationMarker.java create mode 100644 src/proguard/optimize/info/VariableUsageMarker.java create mode 100644 src/proguard/optimize/info/package.html create mode 100644 src/proguard/optimize/package.html create mode 100644 src/proguard/optimize/peephole/BranchTargetFinder.java create mode 100644 src/proguard/optimize/peephole/ClassFinalizer.java create mode 100644 src/proguard/optimize/peephole/ClassMerger.java create mode 100644 src/proguard/optimize/peephole/GotoCommonCodeReplacer.java create mode 100644 src/proguard/optimize/peephole/GotoGotoReplacer.java create mode 100644 src/proguard/optimize/peephole/GotoReturnReplacer.java create mode 100644 src/proguard/optimize/peephole/HorizontalClassMerger.java create mode 100644 src/proguard/optimize/peephole/InstructionSequenceConstants.java create mode 100644 src/proguard/optimize/peephole/InstructionSequenceReplacer.java create mode 100644 src/proguard/optimize/peephole/InstructionSequencesReplacer.java create mode 100644 src/proguard/optimize/peephole/MemberPrivatizer.java create mode 100644 src/proguard/optimize/peephole/MethodFinalizer.java create mode 100644 src/proguard/optimize/peephole/MethodInliner.java create mode 100644 src/proguard/optimize/peephole/NopRemover.java create mode 100644 src/proguard/optimize/peephole/PeepholeOptimizer.java create mode 100644 src/proguard/optimize/peephole/ReachableCodeMarker.java create mode 100644 src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java create mode 100644 src/proguard/optimize/peephole/TargetClassChanger.java create mode 100644 src/proguard/optimize/peephole/UnreachableCodeRemover.java create mode 100644 src/proguard/optimize/peephole/UnreachableExceptionRemover.java create mode 100644 src/proguard/optimize/peephole/VariableShrinker.java create mode 100644 src/proguard/optimize/peephole/VerticalClassMerger.java create mode 100644 src/proguard/optimize/peephole/package.html create mode 100644 src/proguard/package.html create mode 100644 src/proguard/preverify/CodePreverifier.java create mode 100644 src/proguard/preverify/CodeSubroutineInliner.java create mode 100644 src/proguard/preverify/Preverifier.java create mode 100644 src/proguard/preverify/SubroutineInliner.java create mode 100644 src/proguard/retrace/MANIFEST.MF create mode 100644 src/proguard/retrace/ReTrace.java create mode 100644 src/proguard/retrace/package.html create mode 100644 src/proguard/shrink/AnnotationUsageMarker.java create mode 100644 src/proguard/shrink/ClassShrinker.java create mode 100644 src/proguard/shrink/InnerUsageMarker.java create mode 100644 src/proguard/shrink/InterfaceUsageMarker.java create mode 100644 src/proguard/shrink/LocalVariableTypeUsageMarker.java create mode 100644 src/proguard/shrink/ShortestUsageMark.java create mode 100644 src/proguard/shrink/ShortestUsageMarker.java create mode 100644 src/proguard/shrink/ShortestUsagePrinter.java create mode 100644 src/proguard/shrink/Shrinker.java create mode 100644 src/proguard/shrink/SignatureUsageMarker.java create mode 100644 src/proguard/shrink/UsageMarker.java create mode 100644 src/proguard/shrink/UsagePrinter.java create mode 100644 src/proguard/shrink/UsedClassFilter.java create mode 100644 src/proguard/shrink/UsedMemberFilter.java create mode 100644 src/proguard/shrink/package.html create mode 100644 src/proguard/util/AndMatcher.java create mode 100644 src/proguard/util/ArrayUtil.java create mode 100644 src/proguard/util/ClassNameParser.java create mode 100644 src/proguard/util/ConstantMatcher.java create mode 100644 src/proguard/util/EmptyStringMatcher.java create mode 100644 src/proguard/util/ExtensionMatcher.java create mode 100644 src/proguard/util/FileNameParser.java create mode 100644 src/proguard/util/FixedStringMatcher.java create mode 100644 src/proguard/util/ListMatcher.java create mode 100644 src/proguard/util/ListParser.java create mode 100644 src/proguard/util/ListUtil.java create mode 100644 src/proguard/util/NameParser.java create mode 100644 src/proguard/util/NotMatcher.java create mode 100644 src/proguard/util/ObjectUtil.java create mode 100644 src/proguard/util/OrMatcher.java create mode 100644 src/proguard/util/SettableMatcher.java create mode 100644 src/proguard/util/StringMatcher.java create mode 100644 src/proguard/util/StringParser.java create mode 100644 src/proguard/util/VariableStringMatcher.java create mode 100644 src/proguard/util/package.html create mode 100644 src/proguard/wtk/ProGuardObfuscator.java create mode 100644 src/proguard/wtk/default.pro create mode 100644 src/proguard/wtk/package.html diff --git a/README b/README new file mode 100644 index 000000000..2be677723 --- /dev/null +++ b/README @@ -0,0 +1,33 @@ +ProGuard, Java class file shrinker, optimizer, obfuscator, and preverifier +========================================================================== + +This distribution contains the following directories: + +- bin : simple wrapper scripts to run ProGuard, its GUI, and ReTrace +- lib : the main jars, compiled and ready to use with "java -jar ...." +- docs : the complete documentation, licenses, etc. in html format +- examples : some example configuration files +- src : the source code +- build : various alternative build scripts + + +The best place to start is docs/index.html + + +Example +======= + +If you want to give ProGuard a spin right away, try processing the ProGuard +jar itself: + + cd examples + java -jar ../lib/proguard.jar @proguard.pro + +The resulting proguard_out.jar contains the same application, but it's a lot +smaller. + +Enjoy! + +http://proguard.sourceforge.net/ + +Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) diff --git a/bin/proguard.bat b/bin/proguard.bat new file mode 100644 index 000000000..793e7196e --- /dev/null +++ b/bin/proguard.bat @@ -0,0 +1,14 @@ +@ECHO OFF + +REM Start-up script for ProGuard -- free class file shrinker, optimizer, +REM obfuscator, and preverifier for Java bytecode. +REM +REM Note: when passing file names containing spaces to this script, +REM you'll have to add escaped quotes around them, e.g. +REM "\"C:/My Directory/My File.txt\"" + +IF EXIST "%PROGUARD_HOME%" GOTO home +SET PROGUARD_HOME=.. +:home + +java -jar "%PROGUARD_HOME%\lib\proguard.jar" %* diff --git a/bin/proguard.sh b/bin/proguard.sh new file mode 100755 index 000000000..5adc0472e --- /dev/null +++ b/bin/proguard.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# +# Start-up script for ProGuard -- free class file shrinker, optimizer, +# obfuscator, and preverifier for Java bytecode. +# +# Note: when passing file names containing spaces to this script, +# you'll have to add escaped quotes around them, e.g. +# "\"/My Directory/My File.txt\"" + +PROGUARD_HOME=`dirname "$0"`/.. + +java -jar $PROGUARD_HOME/lib/proguard.jar "$@" diff --git a/bin/proguardgui.bat b/bin/proguardgui.bat new file mode 100644 index 000000000..4575aaaff --- /dev/null +++ b/bin/proguardgui.bat @@ -0,0 +1,14 @@ +@ECHO OFF + +REM Start-up script for the GUI of ProGuard -- free class file shrinker, +REM optimizer, obfuscator, and preverifier for Java bytecode. +REM +REM Note: when passing file names containing spaces to this script, +REM you'll have to add escaped quotes around them, e.g. +REM "\"C:/My Directory/My File.txt\"" + +IF EXIST "%PROGUARD_HOME%" GOTO home +SET PROGUARD_HOME=.. +:home + +java -jar "%PROGUARD_HOME%\lib\proguardgui.jar" %* diff --git a/bin/proguardgui.sh b/bin/proguardgui.sh new file mode 100755 index 000000000..c7349067f --- /dev/null +++ b/bin/proguardgui.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# +# Start-up script for the GUI of ProGuard -- free class file shrinker, +# optimizer, obfuscator, and preverifier for Java bytecode. +# +# Note: when passing file names containing spaces to this script, +# you'll have to add escaped quotes around them, e.g. +# "\"/My Directory/My File.txt\"" + +PROGUARD_HOME=`dirname "$0"`/.. + +# On Linux, Java 1.6.0_24 and higher hang when starting the GUI: +# http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7027598 +# We're using the -D option as a workaround. +java -DsuppressSwingDropSupport=true -jar $PROGUARD_HOME/lib/proguardgui.jar "$@" diff --git a/bin/retrace.bat b/bin/retrace.bat new file mode 100644 index 000000000..7201fb67f --- /dev/null +++ b/bin/retrace.bat @@ -0,0 +1,14 @@ +@ECHO OFF + +REM Start-up script for Retrace -- companion tool for ProGuard, free class file +REM shrinker, optimizer, obfuscator, and preverifier for Java bytecode. +REM +REM Note: when passing file names containing spaces to this script, +REM you'll have to add escaped quotes around them, e.g. +REM "\"C:/My Directory/My File.txt\"" + +IF EXIST "%PROGUARD_HOME%" GOTO home +SET PROGUARD_HOME=.. +:home + +java -jar "%PROGUARD_HOME%\lib\retrace.jar" %* diff --git a/bin/retrace.sh b/bin/retrace.sh new file mode 100755 index 000000000..85fd3149b --- /dev/null +++ b/bin/retrace.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# +# Start-up script for Retrace -- companion tool for ProGuard, free class file +# shrinker, optimizer, obfuscator, and preverifier for Java bytecode. +# +# Note: when passing file names containing spaces to this script, +# you'll have to add escaped quotes around them, e.g. +# "\"/My Directory/My File.txt\"" + +PROGUARD_HOME=`dirname "$0"`/.. + +java -jar $PROGUARD_HOME/lib/retrace.jar "$@" diff --git a/build/README b/build/README new file mode 100644 index 000000000..36a235b64 --- /dev/null +++ b/build/README @@ -0,0 +1,40 @@ +ProGuard, Java class file shrinker, optimizer, obfuscator, and preverifier +========================================================================== + +This directory contains a number of alternative ways to build ProGuard: + +- build.sh : a shell script for GNU/Linux +- makefile : a makefile for GNU/Linux +- build.xml : an Ant build file for all platforms +- maven/pom.xml : a Maven POM for building the Maven artifacts + +- As a final alternative, you can also easily compile the code from the + command line: + + mkdir classes + javac -sourcepath src -d classes src/proguard/ProGuard.java + javac -sourcepath src -d classes src/proguard/gui/ProGuardGUI.java + javac -sourcepath src -d classes src/proguard/retrace/ReTrace.java + + For the ProGuard Ant task: + + javac -sourcepath src -d classes -classpath lib/ant.jar \ + src/proguard/ant/ProGuardTask.java + + For the ProGuard Gradle task: + + javac -sourcepath src -d classes -classpath ..... \ + src/proguard/gradle/ProGuardTask.java + + For the Java Micro Edition Wireless Tool Kit (JME WTK) obfuscator plug-in: + + javac -sourcepath src -d classes -classpath wtklib/kenv.zip \ + src/proguard/wtk/ProGuardObfuscator.java + +Note that you'll have to install Ant and the JME WTK yourself. + +Enjoy! + +http://proguard.sourceforge.net/ + +Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) diff --git a/build/build.properties b/build/build.properties new file mode 100644 index 000000000..a6d2bbf5b --- /dev/null +++ b/build/build.properties @@ -0,0 +1,4 @@ +# Ant build properties for ProGuard. + +gradle.home = /usr/local/java/gradle +wtk.home = /usr/local/java/wtk diff --git a/build/build.sh b/build/build.sh new file mode 100755 index 000000000..adb2ee75a --- /dev/null +++ b/build/build.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# +# GNU/Linux build script for ProGuard. + +# +# Configuration. +# + +ANT_HOME=${ANT_HOME:-/usr/local/java/ant} +GRADLE_HOME=${GRADLE_HOME:-/usr/local/java/gradle} +WTK_HOME=${WTK_HOME:-/usr/local/java/wtk} + +if [ -z $PROGUARD_HOME ]; then + PROGUARD_HOME=$(which "$0") + PROGUARD_HOME=$(dirname "$0")/.. +fi + +cd "$PROGUARD_HOME" + +SRC=src +CLASSES=classes +LIB=lib + +PROGUARD=proguard/ProGuard +PROGUARD_GUI=proguard/gui/ProGuardGUI +RETRACE=proguard/retrace/ReTrace +ANT_TASK=proguard/ant/ProGuardTask +GRADLE_TASK=proguard/gradle/ProGuardTask +WTK_PLUGIN=proguard/wtk/ProGuardObfuscator + +ANT_JAR=$ANT_HOME/lib/ant.jar +GRADLE_PATH=\ +$GRADLE_HOME/lib/plugins/gradle-plugins-1.3.jar:\ +$GRADLE_HOME/lib/gradle-base-services-1.3.jar:\ +$GRADLE_HOME/lib/gradle-core-1.3.jar:\ +$GRADLE_HOME/lib/groovy-all-1.8.6.jar +WTK_JAR=$WTK_HOME/wtklib/kenv.zip + +PROGUARD_JAR=$LIB/proguard.jar +PROGUARD_GUI_JAR=$LIB/proguardgui.jar +RETRACE_JAR=$LIB/retrace.jar + +# +# Function definitions. +# + +function compile { + # Compile java source files. + echo "Compiling ${1//\//.} ..." + javac -nowarn -Xlint:none -sourcepath "$SRC" -d "$CLASSES" \ + "$SRC/$1.java" 2>&1 \ + | sed -e 's|^| |' + + # Copy resource files. + (cd "$SRC"; find $(dirname $1) -maxdepth 1 \ + \( -name \*.properties -o -name \*.png -o -name \*.gif -o -name \*.pro \) \ + -exec cp --parents {} "../$CLASSES" \; ) +} + +function createjar { + echo "Creating $2..." + jar -cfm "$2" "$SRC/$(dirname $1)/MANIFEST.MF" -C "$CLASSES" $(dirname $1) +} + +function updatejar { + echo "Updating $PROGUARD_JAR..." + jar -uf "$PROGUARD_JAR" -C "$CLASSES" $(dirname $1) +} + +# +# Main script body. +# + +mkdir -p "$CLASSES" + +compile $PROGUARD +createjar $PROGUARD "$PROGUARD_JAR" + +compile $PROGUARD_GUI +createjar $PROGUARD_GUI "$PROGUARD_GUI_JAR" + +compile $RETRACE +createjar $RETRACE "$RETRACE_JAR" + +if [ -f "$ANT_JAR" ]; then + export CLASSPATH=$ANT_JAR + compile $ANT_TASK + updatejar $ANT_TASK +else + echo "Please make sure the environment variable ANT_HOME is set correctly," + echo "if you want to compile the optional ProGuard Ant task." +fi + +if [ -f "${GRADLE_PATH%%:*}" ]; then + export CLASSPATH=$GRADLE_PATH + compile $GRADLE_TASK + updatejar $GRADLE_TASK +else + echo "Please make sure the environment variable GRADLE_HOME is set correctly," + echo "if you want to compile the optional ProGuard Gradle task." +fi + +if [ -f "$WTK_JAR" ]; then + export CLASSPATH=$WTK_JAR + compile $WTK_PLUGIN + updatejar $WTK_PLUGIN +else + echo "Please make sure the environment variable WTK_HOME is set correctly," + echo "if you want to compile the optional ProGuard WTK plugin." +fi diff --git a/build/build.xml b/build/build.xml new file mode 100644 index 000000000..9544a41a5 --- /dev/null +++ b/build/build.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/makefile b/build/makefile new file mode 100644 index 000000000..264a2ac7f --- /dev/null +++ b/build/makefile @@ -0,0 +1,107 @@ +# GNU/Linux makefile for ProGuard. + +ANT_HOME = /usr/local/java/ant +GRADLE_HOME = /usr/local/java/gradle +WTK_HOME = /usr/local/java/wtk + +PROGUARD_HOME := $(subst ./..,..,$(subst /build/..,/,$(dir $(MAKEFILE_LIST))..)) +SRC = $(PROGUARD_HOME)/src +CLASSES = $(PROGUARD_HOME)/classes +LIB = $(PROGUARD_HOME)/lib + +ANT_JAR = $(ANT_HOME)/lib/ant.jar +GRADLE_JARS = $(GRADLE_HOME)/lib/plugins/gradle-plugins-1.3.jar \ + $(GRADLE_HOME)/lib/gradle-base-services-1.3.jar \ + $(GRADLE_HOME)/lib/gradle-core-1.3.jar \ + $(GRADLE_HOME)/lib/groovy-all-1.8.6.jar +WTK_JAR = $(WTK_HOME)/wtklib/kenv.zip + +NOTHING:= +SPACE:=$(NOTHING) $(NOTHING) +CLASSPATH = $(ANT_JAR):$(subst $(SPACE),:,$(GRADLE_JARS)):$(WTK_JAR) + +PROGUARD = proguard/ProGuard +PROGUARD_GUI = proguard/gui/ProGuardGUI +RETRACE = proguard/retrace/ReTrace +ANT_TASK = proguard/ant/ProGuardTask +GRADLE_TASK = proguard/gradle/ProGuardTask +WTK_PLUGIN = proguard/wtk/ProGuardObfuscator + +TARGETS = $(PROGUARD) $(PROGUARD_GUI) $(RETRACE) $(ANT_TASK) $(GRADLE_TASK) $(WTK_PLUGIN) + +JAVAC_OPTIONS = -nowarn -Xlint:none -classpath $(CLASSPATH) -sourcepath $(SRC) -d $(CLASSES) + +# Command sequence definitions for creating jars. + +define createjar + jar -cfm $(LIB)/$@.jar $(SRC)/$(dir $<)MANIFEST.MF \ + -C $(CLASSES) $(dir $<) +endef + +define updatejar + jar -uf $(LIB)/proguard.jar \ + -C $(CLASSES) $(dir $<) +endef + +# The various targets. + +all: basic options +basic: proguard proguardgui retrace +options: anttask gradletask wtkplugin + +proguard: $(PROGUARD) + $(createjar) + +proguardgui: proguard +proguardgui: $(PROGUARD_GUI) + $(createjar) + +retrace: $(RETRACE) + $(createjar) + +anttask: $(ANT_JAR) +anttask: $(PROGUARD) +anttask: $(ANT_TASK) + $(updatejar) + +gradletask: $(GRADLE_JARS) +gradletask: $(PROGUARD) +gradletask: $(GRADLE_TASK) + $(updatejar) + +wtkplugin: $(WTK_JAR) +wtkplugin: $(PROGUARD) +wtkplugin: $(WTK_PLUGIN) + $(updatejar) + +clean: + -rm -fr $(CLASSES) $(LIB) + + +define RESOURCES + $(shell find $(SRC)/$(dir $(1)) -maxdepth 1 \( -name \*.properties -o -name \*.png -o -name \*.gif -o -name \*.pro \) -printf $(CLASSES)/$(dir $(1))%P\\n) +endef + +define TARGETRULE + $(1): $(CLASSES) $(CLASSES)/$(1).class $(call RESOURCES,$(1)) $(LIB) +endef + +$(foreach TARGET,$(TARGETS),$(eval $(call TARGETRULE,$(TARGET)))) + +$(CLASSES) $(LIB): + -mkdir -p $@ + +$(CLASSES)/%.class: $(SRC)/%.java + javac $(JAVAC_OPTIONS) $^ + +$(CLASSES)/%.properties $(CLASSES)/%.png $(CLASSES)/%.gif $(CLASSES)/%.pro: + cp $(subst $(CLASSES),$(SRC),$@) $@ + +%.jar %.zip: + echo "Please make sure the path to $@ is set" + echo "correctly in this $(strip $(MAKEFILE_LIST))." + echo "Alternatively, if you don't need the corresponding option," + echo "you can run `make' with the option -k." + find $@ + +.PHONY: all basic options proguard proguardgui retrace anttask wtkplugin clean $(TARGETS) $(OPTIONAL_TARGETS) diff --git a/build/maven/ant/pom.xml b/build/maven/ant/pom.xml new file mode 100644 index 000000000..af9af8f75 --- /dev/null +++ b/build/maven/ant/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + net.sf.proguard + proguard-parent + 4.9 + ../pom.xml + + proguard-anttask + [${project.groupId}] ${project.artifactId} + + + ../../../src + + + maven-source-plugin + + + proguard/ant/** + + + + + maven-compiler-plugin + + + proguard/ant/** + + + + + maven-jar-plugin + + + + true + + + + + + maven-javadoc-plugin + + proguard.ant + + + + + + ../../../src + + proguard/ant/** + + + **/*.java + + + + + + + + ${project.groupId} + proguard-base + ${project.version} + + + org.apache.ant + ant + 1.7.0 + provided + + + diff --git a/build/maven/base/pom.xml b/build/maven/base/pom.xml new file mode 100644 index 000000000..2e0ac3a7a --- /dev/null +++ b/build/maven/base/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + net.sf.proguard + proguard-parent + 4.9 + ../pom.xml + + proguard-base + [${project.groupId}] ${project.artifactId} + + + ../../../src + + + maven-source-plugin + + + proguard/gui/** + proguard/ant/** + proguard/gradle/** + proguard/wtk/** + proguard/retrace/** + + + + + maven-compiler-plugin + + + proguard/gui/** + proguard/ant/** + proguard/gradle/** + proguard/wtk/** + proguard/retrace/** + + + + + maven-javadoc-plugin + + proguard.gui:proguard.ant:proguard.gradle:proguard.wtk:proguard.retrace + + + proguard/gui/** + proguard/ant/** + proguard/gradle/** + proguard/wtk/** + proguard/retrace/** + + + + + maven-jar-plugin + + + + proguard.ProGuard + + + + + + + diff --git a/build/maven/gradle/pom.xml b/build/maven/gradle/pom.xml new file mode 100644 index 000000000..6334f7ed7 --- /dev/null +++ b/build/maven/gradle/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + net.sf.proguard + proguard-parent + 4.9 + ../pom.xml + + proguard-gradle + [${project.groupId}] ${project.artifactId} + + + ../../../src + + + maven-source-plugin + + + proguard/gradle/** + + + + + maven-compiler-plugin + + + proguard/gradle/** + + + + + maven-jar-plugin + + + + true + + + + + + maven-javadoc-plugin + + proguard.gradle + + + + + + + + ${project.groupId} + proguard-base + ${project.version} + + + org.gradle + gradle-core + 1.3 + provided + + + org.gradle + gradle-base-services + 1.3 + provided + + + org.codehaus.groovy + groovy-all + 2.0.6 + provided + + + + + gradle + http://repo.gradle.org/gradle/libs-releases-local/ + + + diff --git a/build/maven/gui/pom.xml b/build/maven/gui/pom.xml new file mode 100644 index 000000000..0c1bd0ddd --- /dev/null +++ b/build/maven/gui/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + net.sf.proguard + proguard-parent + 4.9 + ../pom.xml + + proguard-gui + [${project.groupId}] ${project.artifactId} + + + ../../../src + + + maven-source-plugin + + + proguard/gui/** + + + + + maven-compiler-plugin + + + proguard/gui/** + + + + + maven-javadoc-plugin + + proguard.gui + + + + maven-jar-plugin + + + + true + proguard.gui.ProGuardGUI + + + + + + + + ../../../src + + proguard/gui/** + + + **/*.java + MANIFEST.MF + + + + + + + ${project.groupId} + proguard-base + ${project.version} + + + ${project.groupId} + proguard-retrace + ${project.version} + + + diff --git a/build/maven/pom.xml b/build/maven/pom.xml new file mode 100644 index 000000000..f13065c33 --- /dev/null +++ b/build/maven/pom.xml @@ -0,0 +1,162 @@ + + + 4.0.0 + + net.sf.proguard + proguard-parent + 4.9 + pom + [${project.groupId}] ${project.artifactId} + ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. + http://proguard.sourceforge.net/ + + + 3 + + + + + lafortune + Eric Lafortune + http://www.lafortune.eu/ + Saikoa + http://www.saikoa.com/ + + Project Administrator + Developer + + + + + + + GNU General Public License, Version 2 + http://www.gnu.org/licenses/gpl-2.0.txt + repo + + + + + SourceForge.net Tracker + http://sourceforge.net/p/proguard/bugs/ + + + + http://hg.code.sf.net/p/proguard/code + scm:hg:http://hg.code.sf.net/p/proguard/code + + + + UTF-8 + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.5 + 1.5 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8.1 + + + http://download.oracle.com/javase/1.5.0/docs/api/ + + true + + + + attach-javadoc + package + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.1.2 + + + attach-sources + package + + jar + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.1 + + + sign-artifacts + verify + + sign + + + + + + + + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + + + + + base + gui + ant + gradle + retrace + + + + + __wtk_plugin_build__ + + + ${wtk.home}/wtklib/kenv.zip + + + + wtk + + + + diff --git a/build/maven/retrace/pom.xml b/build/maven/retrace/pom.xml new file mode 100644 index 000000000..411df278e --- /dev/null +++ b/build/maven/retrace/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + net.sf.proguard + proguard-parent + 4.9 + ../pom.xml + + proguard-retrace + [${project.groupId}] ${project.artifactId} + + + ../../../src + + + maven-source-plugin + + + proguard/retrace/** + + + + + maven-compiler-plugin + + + proguard/retrace/** + + + + + maven-javadoc-plugin + + proguard.retrace + + + + maven-jar-plugin + + + + true + proguard.retrace.ReTrace + + + + + + + + + ${project.groupId} + proguard-base + ${project.version} + + + diff --git a/build/maven/wtk/pom.xml b/build/maven/wtk/pom.xml new file mode 100644 index 000000000..e8481a439 --- /dev/null +++ b/build/maven/wtk/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + net.sf.proguard + proguard-parent + 4.9 + ../pom.xml + + proguard-wtk-plugin + [${project.groupId}] ${project.artifactId} + + + ../../../src + + + maven-source-plugin + + + proguard/wtk/** + + + + + maven-compiler-plugin + + + proguard/wtk/** + + + + + maven-jar-plugin + + + + true + + + + + + maven-javadoc-plugin + + proguard.wtk + + + + + + ../../../src + + proguard/wtk/** + + + **/*.java + + + + + + + + ${project.groupId} + proguard-base + ${project.version} + + + wtklib + kenv + 2.2 + system + ${wtk.home}/wtklib/kenv.zip + + + diff --git a/docs/FAQ.html b/docs/FAQ.html new file mode 100644 index 000000000..4aa1a4fb5 --- /dev/null +++ b/docs/FAQ.html @@ -0,0 +1,278 @@ + + + + + + +ProGuard FAQ + + + + +

Frequently Asked Questions

+ +

Contents

+ +
    +
  1. What is shrinking?
  2. +
  3. What is obfuscation?
  4. +
  5. What is preverification?
  6. +
  7. What kind of optimizations does ProGuard + support?
  8. +
  9. Can I use ProGuard to process my commercial + application?
  10. +
  11. Does ProGuard work with Java 2? Java 5? Java + 6? Java 7?
  12. +
  13. Does ProGuard work with Java Micro Edition?
  14. +
  15. Does ProGuard work for Google Android + code?
  16. +
  17. Does ProGuard work for Blackberry + code?
  18. +
  19. Does ProGuard have support for Ant?
  20. +
  21. Does ProGuard have support for Gradle?
  22. +
  23. Does ProGuard have support for Maven?
  24. +
  25. Does ProGuard come with a GUI?
  26. +
  27. Does ProGuard handle Class.forName + calls?
  28. +
  29. Does ProGuard handle resource files?
  30. +
  31. Does ProGuard encrypt string constants?
  32. +
  33. Does ProGuard perform control flow + obfuscation?
  34. +
  35. Does ProGuard support incremental + obfuscation?
  36. +
  37. Can ProGuard obfuscate using reserved + keywords?
  38. +
  39. Can ProGuard reconstruct obfuscated stack + traces?
  40. +
+ +

What is shrinking?

+ +Java source code (.java files) is typically compiled to bytecode (.class +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 ProGuard can analyze bytecode and remove +unused classes, fields, and methods. The program remains functionally +equivalent, including the information given in exception stack traces. + +

What is obfuscation?

+ +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 +the bytecode and reverse-engineer entire programs. Sometimes, this is not +desirable. Obfuscators such as ProGuard can remove the debugging +information and replace all names by meaningless character sequences, making +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. + +

What is preverification?

+ +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. ProGuard can +perform the preverification step too, for instance allowing to retarget older +class files at Java 6. + +

What kind of optimizations does ProGuard support?

+ +Apart from removing unused classes, fields, and methods in the shrinking step, +ProGuard can also perform optimizations at the bytecode level, inside +and across methods. Thanks to techniques like control flow analysis, data flow +analysis, partial evaluation, static single assignment, global value numbering, +and liveness analysis, ProGuard can: + + +The positive effects of these optimizations will depend on your code and on +the virtual machine on which the code is executed. Simple virtual machines may +benefit more than advanced virtual machines with sophisticated JIT compilers. +At the very least, your bytecode may become a bit smaller. +

+Some notable optimizations that aren't supported yet: +

+ +

Can I use ProGuard to process my commercial application?

+ +Yes, you can. ProGuard 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. + +

Does ProGuard work with Java 2? Java 5? Java 6? Java 7?

+ +Yes, ProGuard supports all JDKs from 1.1 up to and including 7.0. Java 2 +introduced some small differences in the class file format. Java 5 added +attributes for generics and for annotations. Java 6 introduced optional +preverification attributes. Java 7 made preverification obligatory and +introduced support for dynamic languages. ProGuard handles all versions +correctly. + +

Does ProGuard work with Java Micro Edition?

+ +Yes. ProGuard 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. ProGuard then also performs the required +preverification, producing more compact results than the traditional external +preverifier. +

+ProGuard also comes with an obfuscator plug-in for the JME Wireless +Toolkit. + +

Does ProGuard work for Google Android code?

+ +Yes. Google's dx compiler converts ordinary jar files into files +that run on Android devices. By preprocessing the original jar files, +ProGuard can significantly reduce the file sizes and boost the run-time +performance of the code. It is distributed as part of the Android SDK. +DexGuard, +ProGuard's closed-source sibling for Android, offers additional +optimizations and more application protection. + +

Does ProGuard work for Blackberry code?

+ +It should. RIM's proprietary rapc compiler converts ordinary JME +jar files into cod files that run on Blackberry devices. The compiler performs +quite a few optimizations, but preprocessing the jar files with +ProGuard can generally still reduce the final code size by a few +percent. However, the rapc compiler also seems to contain some +bugs. It sometimes fails on obfuscated code that is valid and accepted by other +JME tools and VMs. Your mileage may therefore vary. + +

Does ProGuard have support for Ant?

+ +Yes. ProGuard provides an Ant task, so that it integrates seamlessly +into your Ant build process. You can still use configurations in +ProGuard's own readable format. Alternatively, if you prefer XML, you +can specify the equivalent XML configuration. + +

Does ProGuard have support for Gradle?

+ +Yes. ProGuard also provides a Gradle task, so that it integrates into +your Gradle build process. You can specify configurations in +ProGuard's own format or embedded in the Groovy configuration. + +

Does ProGuard have support for Maven?

+ +ProGuard's jar files are also distributed as artefacts from the Maven Central repository. There are some third-party +plugins, like the android-maven-plugin that support ProGuard. +DexGuard +also has a compatible Maven plugin. + +

Does ProGuard come with a GUI?

+ +Yes. First of all, ProGuard 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 simplifies creating, +loading, editing, executing, and saving ProGuard configurations. + +

Does ProGuard handle Class.forName calls?

+ +Yes. ProGuard automatically handles constructs like +Class.forName("SomeClass") and SomeClass.class. 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's generally not possible to determine their +possible values. They might be read from a configuration file, for instance. +However, ProGuard will note a number of constructs like +"(SomeClass)Class.forName(variable).newInstance()". These might +be an indication that the class or interface SomeClass and/or its +implementations may need to be preserved. The developer can adapt his +configuration accordingly. + +

Does ProGuard handle resource files?

+ +Yes. ProGuard copies all non-class resource files, optionally adapting +their names and their contents to the obfuscation that has been applied. + +

Does ProGuard encrypt string constants?

+ +No. String encryption in program code has to be perfectly reversible by +definition, so it only improves the obfuscation level. It increases the +footprint of the code. However, by popular demand, ProGuard's +closed-source sibling for Android, DexGuard, does provide string encryption, along with +more protection techniques against static and dynamic analysis. + +

Does ProGuard perform flow obfuscation?

+ +Not explicitly. Control flow obfuscation injects additional branches into the +bytecode, in an attempt to fool decompilers. ProGuard 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. + +

Does ProGuard support incremental obfuscation?

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

Can ProGuard obfuscate using reserved keywords?

+ +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. +Decent decompilers can automatically replace reserved keywords, and the effect +can be undone fairly easily, by obfuscating again with simpler names. + +

Can ProGuard reconstruct obfuscated stack traces?

+ +Yes. ProGuard comes with a companion tool, ReTrace, that can +'de-obfuscate' stack traces produced by obfuscated applications. The +reconstruction is based on the mapping file that ProGuard can write +out. If line numbers have been obfuscated away, a list of alternative method +names is presented for each obfuscated method name that has an ambiguous +reverse mapping. Please refer to the ProGuard User +Manual for more details. + +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/GPL.html b/docs/GPL.html new file mode 100644 index 000000000..c7a24580c --- /dev/null +++ b/docs/GPL.html @@ -0,0 +1,406 @@ + + + +GNU General Public License + + +

GNU General Public License

+

Table of Contents

+ + +

+ +


+ +

+ + + +

GNU GENERAL PUBLIC LICENSE

+

+Version 2, June 1991 + +

+ +
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ + + +

Preamble

+ +

+ The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + +

+

+ When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +

+

+ To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +

+

+ For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +

+

+ We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +

+

+ Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +

+

+ Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + +

+

+ The precise terms and conditions for copying, distribution and +modification follow. + +

+ + +

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

+ + +

+ +0. + This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". +

+ +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +

+ +1. + You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. +

+ +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. +

+ +2. + You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: +

+ +

+ +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. +

+ +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. +

+ +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +

+ +3. + You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + + + +

+ +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. +

+ +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. +

+ +4. + You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +

+ +5. + You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +

+ +6. + Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +

+ +7. + If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. +

+ +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. +

+ +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. +

+ +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +

+ +8. + If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +

+ +9. + The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. +

+ +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +

+ + +10. + If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + + +

NO WARRANTY

+ +

+ +11. + BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +

+ +12. + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +

+ + +

END OF TERMS AND CONDITIONS

+ + diff --git a/docs/GPL_exception.html b/docs/GPL_exception.html new file mode 100644 index 000000000..d610af891 --- /dev/null +++ b/docs/GPL_exception.html @@ -0,0 +1,57 @@ + + + +Special Exception to the GNU General Public License + + +

Special Exception to the GNU General Public License

+ +

+Copyright © 2002-2013 Eric Lafortune +

+ +

+This program is free software; you can redistribute it and/or modify it under +the terms of the GNU 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 +

+ +

+In addition, as a special exception, Eric Lafortune gives permission to link +the code of this program with the following stand-alone applications: +

+and distribute linked combinations including the two. You must obey the GNU +General Public License in all respects for all of the code used other than +these programs. If you modify this file, you may extend this exception to your +version of the file, but you are not obligated to do so. If you do not wish to +do so, delete this exception statement from your version. +

+ + + diff --git a/docs/acknowledgements.html b/docs/acknowledgements.html new file mode 100644 index 000000000..14fdc7467 --- /dev/null +++ b/docs/acknowledgements.html @@ -0,0 +1,88 @@ + + + + + + +ProGuard Acknowledgements + + + + +

Acknowledgements

+ +The first versions of ProGuard grew out of RetroGuard, which its +author Mark Welsh kindly made available under the GNU Lesser General Public +License. RetroGuard 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, 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. +

+ +Dirk Schnelle has contributed and maintained the first versions of the Ant +task. I have rewritten the implementation for version 3.0, but the XML schema +is still based on his work. +

+ +Marcel Patzlaff has initiated a series of artifacts in the Maven Central +repository. I am now maintaining them as part of the official builds. +

+ +Many other people have expressed their enthusiasm and have chimed in with +interesting ideas, bug reports, and bug fixes: Thorsten Heit, Oliver Retzl, +Jonathan Knudsen, Tarcisio Camara, Bob Drury, Dave Jarvis, Marc Chapman, Dave +Morehouse, Richard Osbaldeston, Peter Hawkins, Mark Sherington, David Sitsky, +James Manning, Ptolemy Oberin, Frank-Michael Moser, QZ Shines, Thomas Singer, +Michele Puccini, Roman Bednarek, Natalia Pujol, Daniel Sjöblom, Jan +Filipsky, Charles Smith, Gerrit Telkamp, Noel Grandin, Torbjörn +Söderstedt, Clemens Eisserer, Clark Bassett, Eduard Welch, Dawid Weiss, +Andrew Wilson, Sean Owen, Niels Gron, Ishan Mehta, Steven Adams, Xavier Kral, +Stefan Martin, Toby Reyelts, Bernhard Eder, Manfred Moser, Marco Blümel, +David Reiss, +and many more. Thanks! Your feedback has been invaluable. +

+ +Saikoa is providing the +financial resources for this project. At Saikoa, we're also developing +ProGuard's sibling for Android, +DexGuard. +

+ +SourceForge is providing the resources for hosting this +project and many other projects. +

+ +Sonatype and +the Maven Central repository are hosting the Maven artifacts. +

+ +The code and these web pages were written using Oracle/Sun's JDKs, Linux, +IntelliJ IDEA, GNU emacs, bash, sed, awk, and a whole host of other tools that +continue to make programming interesting. +

+ +And finally, I'm a great fan of Sanaware's Java Docking Library. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/alternatives.html b/docs/alternatives.html new file mode 100644 index 000000000..bbbc47ff4 --- /dev/null +++ b/docs/alternatives.html @@ -0,0 +1,739 @@ + + + + + + +ProGuard Alternatives + + + + +

Alternatives

+ +There are quite a few Java class file shrinkers, optimizers, obfuscators, and +preverifiers out there. Users of ProGuard tell me it easily compares +with the best of them. However, you may want to check that out yourself. +

+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, obfuscators, or preverifiers, or if some information provided +below is incorrect. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Author/CompanyProgramShrink.Optim.Obfusc.Preverif.License
Eric LafortuneProGuardxxxxFree (GPL)
Jochen HoenickeJodexxx
Free (GPL)
Nate NystromBloatxx

Free
Hidetoshi OhuchiJargx
x
Free (BSD)
yWorksyGuardx
x
Free (no source)
MojoMinijarx


Free (Apache)
RiggsHill SoftwareGenJarx


Free (Apache)
ApacheAnt Classfilesetx


Free (Apache)
Carsten Elton SørensenTreeshakerx


Free (Apache)
Jörg SpielerUCDetectorx


Free (EPL)
Romain GuyHarvesterx


Free (BSD)
Emeric VernatDCDx


Free (LGPL)
Cristiano SadunPackx


Free (LGPL)
SableSoot
x

Free (LGPL)
Konstantin KnizhnikJavaGO
x

Free
SableJBCO

x
Free (LGPL)
Jeffrey WheatonClassEncrypt

x
Free (GPL)
Thorsten HeitJavaGuard

x
Free (LGPL)
Patrick MuellerMwobfu

x
Free (GPL)
BebboSoftBb_mug

x
Free (no source)
Vít ŠestákPreverifier


xFree (EPL)
SaikoaDexGuardxxx
Commercial
PreEmptiveDashOProxxx
Commercial
ZelixKlassMasterxxx
Commercial
Sophia CradleSophiaCompressxxx
Commercial
Eastridge TechnologyJshrinkx
x
Commercial
LeeSoftwareSmokescreen Obfuscatorx
x
Commercial
InnaworksmBoosterxx
xCommercial
Sergey SverdlovJ.Class Optimizerxx

Commercial
SmardecAllatori
xx
Commercial
U. of ArizonaSandMark
xx
Commercial
ZenofxClassGuard

x
Commercial
BIS Guard & Co.Java Antidecompiler

x
Commercial
Force 5JCloak

x
Commercial
Semantic DesignsObfuscator

x
Commercial
DuckwareJobfuscate

x
Commercial
ArxanGuardIT

x
Commercial
Vasile CalmatuiVasObfuLite

x
Free
IBM AlphaWorksJAXxxx
(discontinued)
NQ4Jogaxxx
(discontinued?)
Markus JansenJoptxxx
(disappeared?)
Alexander ShvetsCafeBabex
x
(disappeared?)
Brian AllietGcclassx


(disappeared?)
Christian GrothoffJamitx


(disappeared?)
Haruaki TamadaDonQuixote
xx
(disappeared?)
BajieJCMP
xx
(disappeared?)
Elegant SoftwareJMangle

x
(disappeared?)
Eron JokipiiJobe

x
(disappeared?)
JRCDeCaf

x
(disappeared?)
Dr. JavaMarvin Obfuscator

x
(disappeared?)
IBMWSDDxxx
Commercial (discontinued?)
S5 SystemsjPrestoxxx
Commercial (discontinued?)
Plumb DesignCondensityx
x
Commercial (discontinued)
4th PassSourceGuardx
x
Commercial (discontinued?)
CodingArtCodeShieldx
x
Commercial (discontinued?)
Software4jObfuscate4j

x
Commercial (discontinued?)
JAMM ConsultingObfuscatePro

x
Commercial (discontinued?)
JDevelopJSCO

x
Commercial (discontinued?)
4FangJMix

x
Commercial (discontinued?)
RetroLogicRetroGuardx
x
Commercial (disappeared?)
HelsethJObfuscatorx
x
Commercial (disappeared?)
Vega TechnologiesJZipperx
x
Commercial (disappeared?)
JProofJProof

x
Commercial (disappeared?)
ChainKeyJava Code Protector

x
Commercial (disappeared?)
2LKit2LKit Obfuscator

x
Commercial (disappeared?)
WingSoftWingGuard

x
Commercial (disappeared?)
HashJavaHashJava

x
Commercial (disappeared?)
GITSBlurfuscator

x
Commercial (disappeared?)
+

+All trademarks are property of their respective holders. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/android_shades.png b/docs/android_shades.png new file mode 100644 index 0000000000000000000000000000000000000000..96e54f00b73491f769e65eefd9b9320bf3fc4f99 GIT binary patch literal 2726 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 + + + + + +ProGuard Downloads + + + + +

Downloads

+ +ProGuard is distributed under the terms of the GNU General Public +License. Please consult the license page for more +details. +

+ProGuard is written in Java, so it requires a Java Runtime Environment + (JRE 1.5 or higher). +

+You can download the latest release (containing the program jars, the +documentation that you're reading now, examples, and the source code) from this +location: +

+

Download section at SourceForge
+

+ +The proguard section contains major releases and updates with +sub-minor version numbers, for applying emergency fixes. The +proguard beta section contains beta releases. These include +new features and any less urgent bug fixes collected since the previous +release. +

+If you're still working with an older version of ProGuard, check out +the summary of changes below, to see if you're missing something essential. +Better look at the up-to-date on-line version if +you're reading a local copy of this page. Unless noted otherwise, +ProGuard remains compatible across versions, so don't be afraid to +update. +

+If you're only interested in individual jar files for your build process, you +can also download them from the Maven Central repository, with GroupId +net.sf.proguard and ArtifactIds +proguard-parent, +proguard-base, +proguard-gui, +proguard-anttask, +proguard-gradle, +proguard-wtk-plugin, and +proguard-retrace. + +

Mar 2013
Version 4.9

+
    +
  • Added Gradle task. +
  • Added more peephole optimizations for strings. +
  • Improved optimization of classes with static initializers. +
  • Improved processing of finally blocks compiled with JDK 1.4 or older. +
  • Fixed shrinking of access widening abstract methods, for the Dalvik VM. +
  • Fixed overly aggressive shrinking of class annotations. +
  • Fixed processing of unused classes in generic signatures. +
  • Fixed merging of classes with similar class members. +
  • Added java system property optimize.conservatively to allow + for instructions intentionally throwing NullPointerException, + ArrayIndexOutOfBoundsException, or + ClassCastException without other useful effects. +
  • Fixed optimization of unnecessary variable initializations. +
  • Fixed optimization of code involving NaN. +
  • Fixed inlining of methods that are supposed to be kept. +
  • Fixed preverification of artificially convoluted dup constructs. +
  • Fixed quotes for java commands in .bat scripts. +
  • Improved handling of non-sequential line number information. +
  • Now requiring Java 5 or higher for running ProGuard. +
  • Updated build files. +
  • Updated documentation and examples. +
+ +

May 2012
Version 4.8

+
    +
  • Added more peephole optimizations for strings. +
  • Added support for multiple external configuration files in Ant + configurations. +
  • Added support for Ant properties in external configuration files. +
  • Fixed parsing of empty file filters on input and output. +
  • Fixed parsing of '*' wildcard for file filters and name filters. +
  • Fixed obfuscation of private methods that are overridden in concrete + classes with intermediary abstract classes and interfaces (workaround + for Oracle bugs #6691741 and #6684387). +
  • Fixed optimization of complex finally blocks, compiled with JDK 1.4 or + earlier. +
  • Fixed optimizing signatures of methods that are marked as not having + side effects. +
  • Fixed optimization of long local variables possibly causing verification + error for register pairs. +
  • Fixed merging of classes defined inside methods. +
  • Fixed stack consistency in optimization step. +
  • No longer removing debug information about unused parameters, for + -keepparameternames or -keepattributes. +
  • Fixed updating manifest files with carriage return characters. +
  • Now removing unreachable code in preverification step. +
  • Improved default regular expression for stack traces in ReTrace. +
  • Updated documentation and examples. +
+ +

Dec 2011
Version 4.7

+
    +
  • Added support for Java 7. +
  • Parsing unquoted file names with special characters more leniently. +
  • Added support for instance methods overriding class methods. +
  • Added removal of unused parameterless constructors. +
  • Added removal of empty class initializers. +
  • Added peephole optimizations for constant strings. +
  • Avoiding idle optimization passes. +
  • Improved removal of unused constants after obfuscation. +
  • Fixed removal of unused classes referenced by annotations. +
  • Fixed simplifying parameters of constructors that should actually be + preserved. +
  • Fixed simplifying parameters of large numbers of similar constructors. +
  • Fixed exceptions in optimization of unusual obfuscated code. +
  • Fixed NullPointerException when specifying -keepclassmembers + without specific class or class members. +
  • Fixed potential problems with mixed-case class name dictionaries when not + allowing mixed-case class names. +
  • Fixed obfuscation of classes with EnclosingMethod attributes that don't + specify methods. +
  • Fixed preverification of returning try blocks with finally blocks, inside + try blocks, when compiled with JDK 1.4. +
  • Fixed sorting of interfaces containing generics. +
  • Fixed paths in shell scripts. +
  • Fixed filling in of text fields showing class obfuscation dictionary and + package obfuscation dictionary from configuration in GUI. +
  • Worked around Oracle Java 6/7 bug #7027598 that locked the GUI on Linux. +
  • Updated documentation and examples. +
+ +

Feb 2011
Version 4.6

+
    +
  • Added support for synthetic, bridge, and varargs modifiers in configuration. +
  • Added detection of atomic updater construction with constant arguments. +
  • Fixed merging of package visible classes. +
  • Fixed optimization of fields that are only accessed by reflection. +
  • Fixed optimization of read-only or write-only fields that are volatile. +
  • Fixed handling of side-effects due to static initializers. +
  • Fixed handling of bridge flags in obfuscation step. +
  • Fixed handling of super flag when merging classes. +
  • Fixed updating of variable tables when optimizing variables. +
  • Fixed removal of unused parameters with 32 or more parameters. +
  • Fixed incorrect removal of exception handler for instanceof instruction. +
  • Fixed inlining of methods with unusual exception handlers. +
  • Fixed optimization of unusual code causing stack underflow. +
  • Fixed keeping of constructor parameter names. +
  • Fixed unwanted wrapping of non-standard META-INF files. +
  • Fixed filtering of warnings about references to array types. +
  • Fixed overriding of warning option and note option in Ant task. +
  • Improved detection of file name extensions for canonical paths. +
  • Improved printing of seeds specified by -keep options. +
  • Improved printing of notes about unkept classes. +
  • Improved checking whether output is up to date. +
  • Updated documentation and examples. +
+ +

Jun 2010
Version 4.5

+
    +
  • Added option -keepparameternames. +
  • -dontskipnonpubliclibraryclasses is now set by default. Added + -skipnonpubliclibraryclasses as an option. +
  • Made processing independent of order of input classes to get even more + deterministic output. +
  • Improved constant field propagation. +
  • Improved renaming of resource files in subdirectories of packages. +
  • Avoiding making fields in interfaces private. +
  • Optimizing exception handlers for monitorexit instruction. +
  • Reduced maximum allowed code length after inlining from 8000 bytes to + 7000 bytes. +
  • Fixed missing warnings about missing library classes. +
  • Fixed shrinking of annotations with arrays of length 0. +
  • Fixed handling of -0.0 and NaN values when simplifying expressions. +
  • Fixed copying of exception handlers when simplifying tail recursion calls. +
  • Fixed optimization of introspected fields. +
  • Fixed simplification of unnecessary variable initializations. +
  • Fixed evaluation of subroutines in pre-JDK 1.5 code. +
  • Fixed updating of access flags in inner classes information. +
  • Fixed disabling of field privatization. +
  • Fixed invocations of privatized methods. +
  • Fixed updating of local variable debug information in optimization step. +
  • Fixed print settings without file name in GUI. +
  • Fixed field privatization setting in GUI. +
  • Fixed saving incorrectly quoted arguments in GUI. +
  • Fixed handling of regular expressions with only negators. +
  • Fixed unwanted wrapping of non-standard META-INF files. +
  • Fixed regular expression pattern for constructors in ReTrace. +
  • Updated documentation and examples. +
+ +

Jul 2009
Version 4.4

+
    +
  • Added new peephole optimizations. +
  • Added option -optimizations for fine-grained configuration of + optimizations. +
  • Added option -adaptclassstrings for adapting string constants + that correspond to obfuscated classes. +
  • Added option -keeppackagenames for keeping specified package + names from being obfuscated. +
  • Added option -keepdirectories for keeping specified directory + entries in output jars. +
  • Extended options -dontnote and -dontwarn for + fine-grained configuration of notes and warnings. +
  • Added option -regex in ReTrace, for specifying alternative + regular expressions to parse stack traces. +
  • Extended renaming of resource files based on obfuscation. +
  • Improved inlining of constant parameters and removal of unused parameters. +
  • Avoiding bug in IBM's JVM for JSE, in optimization step. +
  • Avoiding ArrayIndexOutOfBoundsException in optimization step. +
  • Fixed configuration with annotations that are not preserved themselves. +
  • Fixed preverification of invocations of super constructors with arguments + containing ternary operators. +
  • Fixed processing of unreachable exception handlers. +
  • Fixed merging of exception classes. +
  • Fixed repeated method inlining. +
  • Fixed inlining of finally blocks surrounded by large try blocks, compiled + with JDK 1.4 or earlier. +
  • Fixed optimization of complex finally blocks, compiled with JDK 1.4 or + earlier. +
  • Fixed obfuscation of anonymous class names, if EnclosingMethod + attributes are being kept. +
  • Fixed obfuscation of inner class names in generic types. +
  • Fixed decoding of UTF-8 strings containing special characters. +
  • Fixed copying of debug information and annotations when merging classes. +
  • Fixed writing out of unknown attributes. +
  • Fixed updating manifest files with split lines. +
  • Updated documentation and examples. +
+ +

Dec 2008
Version 4.3

+
    +
  • Added class merging. +
  • Added static single assignment analysis. +
  • Added support for annotation and enumeration class types in configuration. +
  • Refined shrinking of fields in case of unusual + -keepclassmembers options. +
  • Added simplification of tail recursion calls. +
  • Added new peephole optimizations. +
  • Fixed optimization of unused variable initializations causing negative + stack sizes. +
  • Fixed optimization of unusual initialization code causing + NullPointerExceptions. +
  • Fixed optimization of half-used long and double parameters. +
  • Fixed processing of complex generics signatures. +
  • Working around suspected java compiler bug with parameter annotations on + constructors of non-static inner classes. +
  • Fixed obfuscation of classes with inner classes whose names are preserved. +
  • Fixed access of protected methods in repackaged classes. +
  • Added options -classobfuscationdictionary and + -packageobfuscationdictionary. +
  • Adapting more types of resource file names based on obfuscation. +
  • Extended warnings about incorrect dependencies. +
  • Added start-up scripts and build scripts. +
  • Updated documentation and examples. +
+ +

Mar 2008
Version 4.2

+
    +
  • Refined data flow analysis in optimization step. +
  • Fixed handling of exceptions when inlining subroutines. +
  • Fixed inlining of incompatible code constructs between different java + versions. +
  • Fixed computation of local variable frame size. +
  • Fixed optimization of infinite loops. +
  • Fixed optimization of subroutine invocations. +
  • Fixed optimization of floating point remainder computations. +
  • Fixed removal of unused parameters in method descriptors containing arrays + of longs or doubles. +
  • Added undocumented java system properties + maximum.inlined.code.length (default is 8) and + maximum.resulting.code.length (defaults are 8000 for JSE and + 2000 for JME), for expert users who read release notes. +
  • Fixed processing of generic types in Signature attributes in shrinking and + optimization steps. +
  • Fixed processing of inner class names in Signature attributes in obfuscation + step. +
  • Improved adapting resource file names following obfuscated class names. +
  • Fixed interpretation of package names in GUI. +
  • Fixed default settings for Xlets in GUI. +
  • Updated documentation and examples. +
+ +

Dec 2007
Version 4.1

+
    +
  • Fixed shrinking of default annotation element values. +
  • Fixed optimization of invocations of methods in same class that are + accessed through extensions. +
  • Fixed optimization of invocations of synchronized methods without other + side-effects. +
  • Fixed optimization of some non-returning subroutines. +
  • Fixed handling of local variable debug information when inlining methods. +
  • Avoiding StackOverflowErrors during optimization of complex methods. +
  • Fixed obfuscation of potentially ambiguous non-primitive constants in + interfaces. +
  • Fixed preverification of some code constructs involving String, Class, and + exception types. +
  • The Ant task now allows empty <injars> and + <libraryjars> elements. +
  • Updated documentation and examples. +
+ +

Sep 2007
Version 4.0

+
    +
  • Added preverifier for Java 6 and Java Micro Edition, with new options + -microedition and -dontpreverify. +
  • Added new option -target to modify java version of processed + class files. +
  • Made -keep options more orthogonal and flexible, with option + modifiers allowshrinking, allowoptimization, and + allowobfuscation. +
  • Added new wildcards for class member descriptors: "***", + matching any type, and "...", matching any number of + arguments. +
  • Added support for configuration by means of annotations. +
  • Improved shrinking of unused annotations. +
  • Added check on modification times of input and output, to avoid unnecessary + processing, with new option -forceprocessing. +
  • Added new options -flattenpackagehierarchy and + -repackageclasses (replacing -defaultpackage) to + control obfuscation of package names. +
  • Added new options -adaptresourcefilenames and + -adaptresourcefilecontents, with file filters, to update + resource files corresponding to obfuscated class names. +
  • Added detection of dynamically accessed fields and methods. +
  • Now treating Exceptions attributes as optional. +
  • Now respecting naming rule for nested class names + (EnclosingClass$InnerClass) in obfuscation step, if + InnerClasses attributes or EnclosingMethod + attributes are being kept. +
  • Added new inter-procedural optimizations: method inlining and propagation + of constant fields, constant arguments, and constant return values. +
  • Added optimized local variable allocation. +
  • Added more than 250 new peephole optimizations. +
  • Improved making classes and class members public or protected. +
  • Now printing notes on suspiciously unkept classes in parameters of + specified methods. +
  • Now printing notes for class names that don't seem to be fully qualified. +
  • Added support for uppercase filename extensions. +
  • Added tool tips to the GUI. +
  • Rewritten class file I/O code. +
  • Updated documentation and examples. +
+Upgrade considerations: +
    + +
  • Since ProGuard now treats the Exceptions attribute as + optional, you may have to specify -keepattributes Exceptions, + notably when processing code that is to be used as a library. + +
  • ProGuard now preverifies code for Java Micro Edition, if you specify the + option -microedition. You then no longer need to process the + code with an external preverifier. + +
  • You should preferably specify -repackageclasses instead of the + old option name -defaultpackage. +
+ +

Dec 2007
Version 3.11

+
    +
  • Fixed optimization of invocations of methods in same class that are + accessed through extensions. +
  • Fixed optimization of invocations of synchronized methods without other + side-effects. +
  • Updated documentation and examples. +
+ +

Aug 2007
Version 3.10

+
    +
  • Now handling mixed-case input class names when + -dontusemixedcaseclassnames is specified. +
  • Fixed optimization of synchronization on classes, as compiled by Eclipse + and Jikes. +
  • Fixed optimization of switch statements with unreachable cases. +
  • Avoiding merging subsequent identically named files. +
  • Updated documentation and examples. +
+ +

Jun 2007
Version 3.9

+
    +
  • Fixed processing of .class constructs in Java 6. +
  • Fixed repeated processing of .class constructs. +
  • Fixed possible division by 0 in optimization step. +
  • Fixed handling of variable instructions with variable indices larger than + 255. +
  • Updated documentation and examples. +
+ +

Mar 2007
Version 3.8

+
    +
  • Fixed optimization of parameters used as local variables. +
  • Fixed obfuscation with conflicting class member names. +
  • Fixed incremental obfuscation with incomplete mapping file for library jars. +
  • Updated documentation and examples. +
+ +

Dec 2006
Version 3.7

+
    +
  • Now accepting Java 6 class files. +
  • Fixed shrinking of partially used annotations. +
  • Improved incremental obfuscation, with new option + -useuniqueclassmembernames. +
  • Printing more information in case of conflicting configuration and input. +
  • Fixed optimization of repeated array length instruction. +
  • Fixed optimization of subsequent try/catch/finally blocks with return + statements. +
  • Fixed optimization of complex stack operations. +
  • Fixed optimization of simple infinite loops. +
  • Fixed optimization of expressions with constant doubles. +
  • Tuned optimization to improve size reduction after preverification. +
  • Fixed overflows of offsets in long code blocks. +
  • Now allowing class names containing dashes. +
  • Updated documentation and examples. +
+ +

May 2006
Version 3.6

+
    +
  • No longer automatically keeping classes in parameters of specified methods + from obfuscation and optimization (introduced in version 3.4). +
  • Fixed inlining of interfaces that are used in .class constructs. +
  • Fixed removal of busy-waiting loops reading volatile fields. +
  • Fixed optimization of comparisons of known integers. +
  • Fixed optimization of known branches. +
  • Fixed optimization of method calls on arrays of interfaces. +
  • Fixed optimization of method calls without side-effects. +
  • Fixed optimization of nested try/catch/finally blocks with return + statements. +
  • Fixed initialization of library classes that only appear in descriptors. +
  • Fixed matching of primitive type wildcards in configuration. +
  • Fixed the boilerplate specification for enumerations in the GUI. +
  • Updated documentation and examples. +
+ +

Jan 2006
Version 3.5

+
    +
  • Fixed obfuscation of class members with complex visibility. +
  • Fixed optimization bugs causing stack verification errors. +
  • Fixed optimization bug causing overridden methods to be finalized. +
  • Fixed optimization bug causing abstract method errors for retro-fitted + library methods. +
  • Fixed optimization bug evaluating code with constant long values. +
  • Fixed bug in updating of optional local variable table attributes and local + variable type table attributes after optimization. +
  • Fixed interpretation of comma-separated class names without wildcards. +
  • Updated documentation and examples. +
+ +

Oct 2005
Version 3.4

+
    +
  • Extended optimizations: removing duplicate code within methods. +
  • Extended regular expressions for class names to comma-separated lists. +
  • Now automatically keeping classes in descriptors of kept class members. +
  • Added verbose statistics for optimizations. +
  • Added boilerplate Number optimizations in GUI. +
  • Fixed Class.forName detection. +
  • Fixed incremental obfuscation bug. +
  • Fixed optimization bug causing stack verification errors. +
  • Fixed optimization bugs related to removal of unused parameters. +
  • Fixed exception when optimizing code with many local variables. +
  • Fixed exception when saving configuration with initializers in GUI. +
  • Updated documentation and examples. +
+ +

Jun 2005
Version 3.3

+
    +
  • Extended optimizations: making methods private and static when possible, + making classes static when possible, removing unused parameters. +
  • Made file names relative to the configuration files in which they are + specified. Added -basedirectory option. +
  • Added -whyareyoukeeping option to get details on why given + classes and class members are being kept. +
  • Added warnings for misplaced class files. +
  • Improved printing of notes for Class.forName constructs. +
  • Implemented 'assumenosideeffects' nested element in Ant task. +
  • Improved processing of annotations. +
  • Fixed reading and writing of parameter annotations. +
  • Fixed various optimization bugs. +
  • Fixed wildcards not matching '-' character. +
  • Fixed wildcard bug and checkbox bugs in GUI. +
  • Setting file chooser defaults in GUI. +
  • Leaving room for growBox in GUI on Mac OS X. +
  • Properly closing configuration files. +
  • Updated documentation and examples. +
+ +

Dec 2004
Version 3.2

+
    +
  • Fixed JDK5.0 processing bugs. +
  • Fixed optimization bugs. +
  • Fixed relative paths in Ant task. +
  • Improved speed of shrinking step. +
  • Updated documentation and examples. +
+ +

Nov 2004
Version 3.1

+
    +
  • Improved obfuscation and shrinking of private class members. +
  • Added inlining of interfaces with single implementations. +
  • Added option to specify obfuscation dictionary. +
  • Added option to read package visible library class members. +
  • Extended support for JDK5.0 attributes. +
  • Fixed various optimization bugs. +
  • Modified Ant task to accept paths instead of filesets. +
  • Fixed two Ant task bugs. +
  • Updated documentation and examples. +
+ +

Aug 2004
Version 3.0

+
    +
  • Added bytecode optimization step, between shrinking step and obfuscation + step. +
  • Generalized filtered recursive reading and writing of jars, wars, ears, + zips, and directories. +
  • Added support for grouping input and output jars, wars, ears, zips, and + directories. +
  • Added support for applying mapping files to library classes. +
  • Removed -resourcejars option. Resources should now be read + using regular -injars options, using filters if necessary. +
  • Rewrote Ant task. Input and output modification dates are not checked at + the moment. Minor changes in XML schema: +
      +
    • Filters now specified using attributes. +
    • 'outjars' now nested element instead of attribute. +
    • 'type' attribute of <method> element no + longer defaults to 'void'. +
    • < and > characters now have to be + encoded in embedded configurations. +
    • <proguardconfiguration> task no longer accepts + attributes. +
    +
  • Updated J2ME WTK plugin, now customizable through configuration file. +
  • Updated GUI. +
  • Fixed various processing bugs. +
  • Fixed ReTrace parsing bugs. +
  • Improved jar compression. +
  • Updated documentation and examples. +
+ +

Mar 2004
Version 2.1

+
    +
  • Added support for JDK1.5 classes. +
  • Added additional wildcard for matching primitive types. +
  • Added possibility to switch off notes about duplicate class definitions. +
  • Fixed use of multiple filters on output jars. +
  • Fixed option to keep all attributes. +
  • Fixed various Ant task bugs. +
  • Updated documentation and examples. +
+ +

Dec 2003
Version 2.0

+
    +
  • Added a graphical user interface for ProGuard and ReTrace. +
  • Added -applymapping option for incremental obfuscation. +
  • Added support for filtering input and output files. +
  • Added support for the J++ SourceDir attribute. +
  • Improved detection of .class constructs. +
  • Improved handling of misplaced manifest files. +
  • Improved implementation of ReTrace. +
  • Worked around String UTF-8 encoding bug affecting foreign characters. +
  • Fixed exception when ignoring warnings. +
  • Fixed various Ant task bugs. +
  • Updated documentation and examples. +
+ +

Aug 2003
Version 1.7

+
    +
  • Fixed various Ant task bugs. +
  • Fixed ClassCastException due to explicitly used abstract classes with + implicitly used interfaces targeted at JRE1.2 (the default in JDK1.4). +
  • Fixed -defaultpackage bug for protected classes and class + members. +
  • Fixed ReTrace bug when retracing without line number tables. +
  • Worked around zip package problems with duplicate out entries and rogue + manifest files. +
  • Added work-around for handling malformed legacy interface class files. +
  • Updated documentation and examples. +
+ +

May 2003
Version 1.6

+
    +
  • Added support for Ant. +
  • Added support for the J2ME Wireless Toolkit. +
  • Added support for reading and writing directory hierarchies. +
  • Added option for specifying resource jars and directories. +
  • Added support for wildcards in class member specifications. +
  • Improved handling of the -defaultpackage option. +
  • Improved stack trace parsing in ReTrace tool. +
  • Fixed processing of libraries containing public as well as non-public + extensions of non-public classes. +
  • Fixed examples for processing libraries, midlets, and serializable code. +
  • Updated documentation and examples. +
+ +

Jan 2003
Version 1.5

+
    +
  • Fixed processing of retrofitted library interfaces. +
  • Fixed processing of .class constructs in internal classes + targeted at JRE1.2 (the default in JDK1.4). +
  • Fixed -dump option when -outjar option is not + present. +
  • Updated documentation and examples. +
+ +

Nov 2002
Version 1.4

+
    +
  • Now copying resource files over from the input jars to the output jar. +
  • Added option to obfuscate using lower-case class names only. +
  • Added better option for obfuscating native methods. +
  • Added option not to ignore non-public library classes. +
  • Added automatic .class detection for classes compiled with + Jikes. +
  • Updated documentation and examples. +
+ +

Sep 2002
Version 1.3

+
    +
  • Added support for wildcards in class names. +
  • Added tool to de-obfuscate stack traces. +
  • Added options to print processing information to files. +
  • Added option to rename source file attributes. +
  • Fixed processing of implicitly used interfaces targeted at JRE1.2 (the + default in JDK1.4) +
  • Fixed processing of configurations with negated access modifiers. +
  • Fixed duplicate class entry bug. +
  • Updated documentation and examples. +
+ +

Aug 2002
Version 1.2

+
    +
  • Improved speed. +
  • Fixed processing of classes targeted at JRE1.2 (the default in JDK1.4) + with references to their own subclasses. +
  • Fixed processing of static initializers in J2ME MIDP applications. +
  • Fixed processing of retrofitted interfaces (again). +
  • Added more flexible handling of white space in configuration. +
  • Updated documentation. +
+ +

Jul 2002
Version 1.1

+
    +
  • Added automatic detection of Class.forName("MyClass"), + MyClass.class, and + (MyClass)Class.forName(variable).newInstance() constructs. + This greatly simplifies configuration. +
  • Added options to keep class names and class member names without affecting + any shrinking. They are mostly useful for native methods and serializable + classes. +
  • Fixed processing of retrofitted interfaces. +
  • Added handling of missing/invalid manifest file in input jar. +
  • Updated documentation and examples. +
+ +

Jun 2002
Version 1.0

+
    +
  • First public release, based on class parsing code from Mark Welsh's + RetroGuard. +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/drop1.gif b/docs/drop1.gif new file mode 100644 index 0000000000000000000000000000000000000000..426d8564123ebdfa0ff1cc6cce776cfc938cebb3 GIT binary patch literal 803 zcmZ?wbh9u|oWRJzaEyUr?b@}=moHzsbm`o=bEi+AK6UEU-rnBM&d$ch#@gE2%F4>} z^77Ks(trQ{{rU6f*RNkce*F0M?c0|xUp{^M^x?yYckkZ4dGqGgt5?sSJ$v%x$)iV) zZr!?d(|epKY#rA@%{VvFJHcV{`~pVr%xY0e*Ey^!@GCy-oAbN z=FOW|uU@@;`SRJbXOAC0e)#a=g9i`p-@kwF-n~0_?%ce2Gs92=Iv@vw;(~$wUxS#r zjlH5OD?0}#7q?a)&xDDSCQq3$P>&)zuN3v+kbSy88P1gu`_PuM*wa#dMVp zOh`;*>y_%1%sDZ2^}=;`cUOFVb@%l4_4oHTFmucK?AY+|aEGvV+?gF4lX;7CQ&wy| zbdZ@tP{pUi(Xs9Mc~PfsIp1AdUS3`iygKgeuC1@HZ%95p&v*B>x3_l`zh3v@rg1Bq zww8UyMW&|i$0uT4BF^pE`T6;U#oqJ%_U`)n`o`w$`{(xV{{H^K;cj_N8yOg^ E0oW#P7ytkO literal 0 HcmV?d00001 diff --git a/docs/drop2.gif b/docs/drop2.gif new file mode 100644 index 0000000000000000000000000000000000000000..b6075421d8333ffca319124594b8042af52ba26f GIT binary patch literal 620 zcmZ?wbh9u|^Xu2IA3uJ4`}Xb2moJ|_efsd>!@GCy-n@D9>eZ`f&z?Pb^5oH@M|bYr zxpnK-wQJX|T)A@T(xo$J&Kx*!VBfxdn>KA)wQALh6)To4TefJ?qDhk`O_(sDudlDB zrlzc{?C;;dzkmP!`Sa)Z@87?E{rdUy=Z_yhzJLGz<;#~(pFVy3`0>Ms4{zVTee>qc z%a<>oJ$v@}@#BXNA3k{S;Qsyl_wL=hbLY;@n>RBIO`rpEHYhF_*e^DS8XF4e>FV&9 zi;Ef==<(`USxAZ*3GnIIT3ecl8uIHZDOgL38yoPcDcM??iwf$osyfO`nF#T-GdtN> ziW}*3urS!lm{PpwuzxO}>3QJswJ6Ol^QuR)HK%#RShdfh>#KuPso!sh9don&M z9PgKN4q~wkdg3-&DmX2sb7Si9Ne2GMY^J}H^R2zLeaJn&mKK` zbnDiw8#iuj+O%oWqDBAz|Nr~<@9*EgfByXW_3PKqpFe;6`0@Sw_YWUFynXxj<;$1P zo;`c`@ZtUY_cOqN4u}D=i-FZ-!R4Hrr6NIxGrg014=po1mBJ;m=YBnnB*pDvHYq4I?x&bsIX+_ literal 0 HcmV?d00001 diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3923ec14ef4a57a612bc398a3569c26f7904397f GIT binary patch literal 7406 zcmeHLX;)Lp*4|+Z7zi0i0!bJ`m}dgC;=rT`B7=w#gd~I^2_ZlrVJ0L*85Cr+)z%gj zwG~AWK}84+NVh%D^K5tjf&1}(xa;+;3Iq}D+x_LP`>yqNt#wj4=RAAYu6^p%+0O$& z0BeYd0MrxVhBH7c0C;(=of83mM%P?iR?ii-0G&*L0E9s%P=pR_XKoIwo%xsW?Elo% z6iiM|!tL9);nuBNFg7*@*RNlPYuB#9l`B`^^5x5L@!~}oxiA73E?j`~=g-4yue}Cm z&YXc)UwsvZhlgQsa1i?Y`{CrtlhD)C1ILdahpw(J=;-KxqeqWIdwVX#(9qBTb#-;1(P*HirUt63tD&;85-KVxpuD^s_6F=fmN{hvCqnLvZlmK{#;W0AyulK}JRfq^GCD-o1Na&z?PyoSY1aiHWdn z+ct=aiGj$-Ncioy-{8rUC-C#nKf{kd{s@7Af$;tJ--Adj27iBl`1adx!PnOpzW(}a z`0~py!Q0y#KL7l4@bvVAPe1(>xLhuL{PD-YX0zeL4?l$W-+v#NOeVbh?z`aX>I#n^ zKZdv7dJ7&sdIU>LOYr8KZ-U8W0tW{Nn46n}+1XjRfB!zr%*?>uyLaKvojWiwF#+S_ z<8brlO}KI62E6gc8*ugNRTv!|g-e$%!N|x6y#D&@aPHhWID7UioIZUTPMtahLqkI_ zFfagpeSOf|+Y2X7oPh4`Zs_dngk#5!fzfCLgTVl8ZEeun+6v9h&CuA`2=(>#pw()j zwzd}3YBf|G8vSXmO^oHF%%UQff(hcu&@x09617cd3hid3L!T) z7jklPAUiu7GBYz_|Ni~3d-rZgNJxNPyLQ2j9Xnv_)~yg58w*iUQSkfkzoWGr0l)tG zEBx}yFAx$E0zduq6a4VQ5AfZ0-+`Z>AAIx8H^Arf;j6E{0v?YCUwrWeTJK))*=L`@ zC!c%*91aIQ`sgG0;DZl<#bUvG@4W{M1_R!C=N)+a?YDtSrNYX}3VJ78V0n2NoSmIv zad8nyBoZtvEWrHyJP?UQc=+%k*xA{^g9i`b-o1M;Jw5&GrTBmQ4?U^%_NPf$N3Q?* zcP1EgDtbsD5DX2U`cn%85JdKcg`({wwD)9Re^j)^Mbb4=}j)36ssMR4hj1U{q z%{S=SZp=C@T|=YMG<4TGCabB1>)FolJl-3*I*Vrp5dp?Fb|XVet5LgDlgZW9Y6_L6 zWi&Lk=)8;_-J$)%IHq;{Ko?KfR8Ob6c#((-JKM4{8>^M&g1HJhgXP}p$@2{e?Kh9G zW^k&T$8D^ub#Yd8ZQr-_MvvaZ5Sn>+$E&UCS}oI!)7Fj*BF%GHH3&9x8?`l6DkV`a zD~nkgYUk=BhKNKZsj|98!)Rk&ozdNgX)m5xNaveJ@bk2ho6TRB~IHnwu2 zQdLEv)gc33$MJA;TJZqg`X;T~NoikZUA$5xalaSa-oo-!6G}>D_6{UxH8S9M8aspg zaYP6{;M2mOR65w9IZDK$g@!{h?dPU-;~A~01+heGWn)LIaHeRO?z;Alpgx=<06tq=?I1_R2~%y`e>awEJW< z!gsjSoD`)ii%0HH=S+;>O0ULvO6Bt8ln@y#OG{)#5*ZDk<8}7nXJBMe9A(AJraWQJ zL`Hhr^(q{XTDCBqH+RHj5=jW9a%Ck&%ifze$>laHqWqbuiHx!9*HWAizLlXS zJ5^DYgk?#Qq}Y~7s-`j7p8UW*^awm%ofRb#Q(o?YjIq>|47`slQW$Hn9k{65TW^%I9ucss@URK_YZ0CCPxTmJwnsCTo;@K<| zNXsiJu5MhOe=k}S9vYR5u$V7AkdbyRIWgg4`Tmf0PP30)%JuZ|1NMbUJY~@%xk^oE zwR-!73NW5zD;3S=PTabFWi;X92=Qi6JDcfYoqP=qFeO+TW|KSZ@&fwsdN$H4%8E>fbH>wBMib&k99I3W&3Ac!l)=N(7);T+ zdH4iInqM$hm7IVRnx1+kF(F=gcNoRPDOt@Y?R21C?>p#MQ`f9N7A(N;BeYQ+N+n0o z0BFXE369xO26wu9PA!IU+ZX7RIs+ z^vX;RqDpRKyLWZlF(=2W+IqIZFU;Z{;eF7ewvrUg7N8v^A60t!bEbtw$s;j(yX=&e zHFZsG{1ZbP@ZhC!l9v=M9+^EXoX(xf%@xk%<(n2CA-IE+T8pC@*a%1EKfRKOHWd-t zu}~$32SD48l?{4#5ga|*i09!bBu2F(+T@Cs7exyTqD8R;ZB{acqca7=yIz2!Ni@|~ zI>^zcOOUQe3B@HgGKJC!Z&e;B7IZT7qP2X z+E{c7xl%;_x8{FC46oTVCfC`9C5JCJ-+;(YzFN7CF3sP>l@;Gg3mX^Z<*KT`C9T|$qP!6 zIZx|(d$N{COI|H`Udub?sb1(vK*u^ywLvGAhGMm}zrs`h+x+yea?`Z5v~_;EJ9)R6 zpT@_>!_J*MVf*&&W{w&j9&YBSp`oE>o*En+Z04$%r&>5F=BGYBKF@Jf%uhFRQ#Us^ zGe5=L6!TJ4VVb!qg+ej&Q!<$hXj_9n;CV4V+OLa5Pvi`B=x+|4xJSY^hZYaN0||44?PcfxIMBrxGSKeqcbSDrw?OJ zKiq&wi|gt1HR#*C+Pn??V_l&U0|=0|4oD35H?%gpyVbMm8`#aBdc=eJkc&%efV9w~ zx+a!}PE)ICbPcnCqciviW5BA1f*3=y77;em7Dq=%g~E}_KwQcg6h5#P!?2Ofpg7lb z4n{ZDvVy|-G)iq9N9P-aJe)=&95go7kheIL*}CjK-s~MvMpTf!m+2bsrJ+&P^=$$Df<#KGNqGDEXu_`XA(S8Uef1fg8=FaU({~MI?bZ;B z@+QZwCcU1C@0KL0_UaC%yp zcVtd%vbHNHGn)OvEh~J8=1?r2%@gM4PS4~QN~C4z>9KjCiP*cj?J%h9ZHi3uqIpx1 zv{a_7(lqg7EY=ut&%b&L5wZH>k}{&B6IILQZ!K6gwftuEXeiDV6)G}?Uf0ro>Y(NN zvwnA?hkJd!d$YIy-kZ;^KK%#z=~Dy2T&AI#BOcWwWPCh9<;4=JT<~E)C4}XGKWP>m z?w8i=pCDfrzjzFDse;DDW8*r-1sz`KSg%ty)F$ZSdfQy5Safu(p}$Nk|EoIrMvWZn z;}&gvW6iNy1GnhkSog*{H?AG9p1oP8w$uW+4!}A!)~7dW)0=f^tVd%l+M+>Y{rQi+ z@mP1Z=*?JTw&=?iP1&L+V=Z~3j+~mBYSxhPHyUflmhUpwiLpkEzrUOHU^<=t+&6Z! z=KHtKyS_~S{_`KoK$wun^YIaet-6={Ww71%aWb-fgmQF6et<>9__^)ht-p6x_4my%Ya&yQp%yQY4 zOhf1CDYRs^P-I_L7?9w$qacL6pGnOK1Fm(rt=HkqWRF7`@hm@p(mmF) zlF;x-!8Rh@8&9B=Yabg;u!(a_$;GFri87qxlUxrL;s5_cT=o!k%` literal 0 HcmV?d00001 diff --git a/docs/feedback.html b/docs/feedback.html new file mode 100644 index 000000000..dd9c5091b --- /dev/null +++ b/docs/feedback.html @@ -0,0 +1,118 @@ + + + + + + + +ProGuard Feedback + + + + +

Feedback

+ +By now, I've invested an enormous amount of time in ProGuard. You can +help by providing feedback! If you have problems, bugs, bug fixes, ideas, +encouragements, etc., please let me know: +

+

    +
  • Through + Saikoa, + we provide professional support for ProGuard. If you find ProGuard useful + and you would like to have some professional backing, this is the place to + go.
  • + +
  • The help forum (at SourceForge) + and Stack Overflow are common places to ask questions about + any problems you might have configuring and running ProGuard. At this + time, I can generally only assist other open source projects though. If + you're working on commercial software, please consider our professional + support above.
  • + +
  • The open discussion forum (at SourceForge) offers a place + to share your thoughts and discuss new ideas.
  • + +
  • The bug + tracking page (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.
  • + +
  • The feature request page (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.
  • + +
  • The download section at SourceForge and the project page at Freecode (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.
  • + +
  • For anything that doesn't fall in the above categories, you can mail me + directly at + + + +.
  • +
+

+I can't promise a swift answer, or any answer at all, for that matter, but it's +always great to see constructive comments. +

+ +ProGuard isn't a typical open source project, in the sense that I am +not looking for code contributions. Developing on my own allows me to +do things my way, without the overhead and compromises associated with larger +projects. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 000000000..59499b0a0 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,92 @@ + + + + + + + + + + + +ProGuard + + + + + + + +<body> +<p class="intro"> +<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> +Your browser doesn't support frames, but that's cool. +<p> +You can go straight to the <a href="main.html">main page</a>. + +<hr /> +<address> +Copyright &copy; 2002-2013 +<a target="other" href="http://www.lafortune.eu/">Eric Lafortune</a>. +</address> +</body> + + diff --git a/docs/license.html b/docs/license.html new file mode 100644 index 000000000..d1077b8e7 --- /dev/null +++ b/docs/license.html @@ -0,0 +1,61 @@ + + + + + + +ProGuard License + + + + +

License

+ +ProGuard 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. +

+ +ProGuard itself is copyrighted, but its distribution license provides +you with some rights for modifying and redistributing its code and its +documentation. More specifically, ProGuard is distributed under the +terms of the GNU General Public License (GPL), version +2, as published by the Free +Software Foundation (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 +ProGuard, the program as a whole has to be distributed at no charge +under the GPL. I am granting a special +exception to the latter clause (in wording suggested by +the FSF), for combinations with the following stand-alone +applications: Apache Ant, Apache Maven, the Google Android SDK, the Eclipse +ProGuardDT GUI, the EclipseME JME IDE, the Oracle NetBeans Java IDE, the +Oracle JME Wireless Toolkit, the Intel TXE SDK, the Simple Build Tool for +Scala, the NeoMAD Tools by Neomades, the Javaground Tools, and the Sanaware +Tools. + +

+The ProGuard user documentation represents an important part of this +work. It may only be redistributed without changes, along with the unmodified +version of the code. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/main.html b/docs/main.html new file mode 100644 index 000000000..988d87ca7 --- /dev/null +++ b/docs/main.html @@ -0,0 +1,104 @@ + + + + + + + + + +ProGuard Main + + + + +

Main

+ +

+ProGuard 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 higher, or for Java +Micro Edition. +

+Some uses of ProGuard are: +
    + +
  • Creating more compact code, for smaller code archives, faster transfer + across networks, faster loading, and smaller memory footprints.
  • + +
  • Making programs and libraries harder to reverse-engineer.
  • + +
  • Listing dead code, so it can be removed from the source code.
  • + +
  • Retargeting and preverifying existing class files for Java 6 or higher, to + take full advantage of their faster class loading.
  • + +
+

+ProGuard's main advantage compared to other Java obfuscators is +probably its compact template-based configuration. A few intuitive command +line options or a simple configuration file are usually sufficient. +The user manual explains all available options and shows examples of this +powerful configuration style. +

+ProGuard is fast. It only takes seconds to process programs and +libraries of several megabytes. The results section presents actual figures +for a number of applications. +

+ProGuard is a command-line tool with an optional graphical user +interface. It also comes with plugins for Ant, for Gradle, and for the JME +Wireless Toolkit. +

+

+ + +ProGuard now has a sibling optimizer and obfuscator for Android: +DexGuard. It +focuses on code protection, with additional features like string encryption +and class encryption. It directly targets Dalvik bytecode and streamlines the +Android build process. +

+The following sections provide more detailed information: +
    +
  • Main: this overview page.
  • +
  • Results: some results obtained with + ProGuard, including timings and memory usage.
  • +
  • FAQ: answers to some Frequently Asked Questions.
  • +
  • Manual: the complete ProGuard user + manual, with examples and troubleshooting tips.
  • +
  • Quality: a discussion of the (excellent) quality + of ProGuard's code.
  • +
  • Screenshots: some impressions of what ProGuard looks like.
  • +
  • Testimonials: what users think of + ProGuard.
  • +
  • License: ProGuard is free, under a GPL + license.
  • +
  • Downloads: download the ProGuard + package yourself.
  • +
  • Feedback: tell me about your experiences, or + learn from others on our forums.
  • +
  • Acknowledgements: people who have been + helpful.
  • +
  • Alternatives: other Java obfuscators, + optimizers, and shrinkers.
  • +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/ant.html b/docs/manual/ant.html new file mode 100644 index 000000000..0837bbb43 --- /dev/null +++ b/docs/manual/ant.html @@ -0,0 +1,644 @@ + + + + + + +Ant Task + + + + +

Ant Task

+ +ProGuard can be run as a task in the Java-based build tool Ant (version +1.6.0 or higher). +

+ +Before you can use the proguard task, you have to tell Ant about +this new task. The easiest way is to add the following line to your +build.xml file: +

+ +

+<taskdef resource="proguard/ant/task.properties"
+         classpath="/usr/local/java/proguard/lib/proguard.jar" />
+
+

+ +Please make sure the class path is set correctly for your system. +

+ +There are three ways to configure the ProGuard task: +

    +
  1. using an external configuration file,
  2. +
  3. using embedded ProGuard configuration options, or
  4. +
  5. using the equivalent XML configuration tags.
  6. +
+These three ways can be combined, depending on practical circumstances and +personal preference. +

+ +

1. An external ProGuard configuration file

+ +The simplest way to use the ProGuard task in an Ant build file is to keep your +ProGuard configuration file, and include it from Ant. You can include your +ProGuard configuration file by setting +the configuration +attribute of your +proguard task. Your ant build file will then look like this: +

+ +

+<taskdef resource="proguard/ant/task.properties"
+         classpath="/usr/local/java/proguard/lib/proguard.jar" />
+<proguard configuration="myconfigfile.pro"/>
+
+

+ +This is a convenient option if you prefer ProGuard's configuration style over +XML, if you want to keep your build file small, or if you have to share your +configuration with developers who don't use Ant. +

+ +

2. Embedded ProGuard configuration options

+ +Instead of keeping an external ProGuard configuration file, you can also copy +the contents of the file into the nested text of the proguard task +(the PCDATA area). Your Ant build file will then look like this: +

+ +

+<taskdef resource="proguard/ant/task.properties"
+         classpath="/usr/local/java/proguard/lib/proguard.jar" />
+<proguard>
+  -libraryjars ${java.home}/lib/rt.jar
+  -injars      in.jar
+  -outjars     out.jar
+
+  -keepclasseswithmembers public class * {
+      public static void main(java.lang.String[]);
+  }
+</proguard>
+
+

+ +Some minor syntactical changes are required in order to conform with the XML +standard. +

+ +Firstly, the # character cannot be used for comments in an XML +file. Comments must be enclosed by an opening <!-- and a +closing -->. All occurrences of the # character +can be removed. +

+ +Secondly, the use of < and > characters would +upset the structure of the XML build file. Environment variables can be +specified with the usual Ant style ${...}, instead of the ProGuard +style <...>. Other occurrences of < and +> have to be encoded as &lt; and +&gt; respectively. +

+ +

3. XML configuration tags

+ +If you really prefer a full-blown XML configuration, you can replace the +ProGuard configuration options by XML configuration tags. The resulting +configuration will be equivalent, but much more verbose and difficult to read, +as XML goes. The remainder of this page presents the supported tags. For a +more extensive discussion of their meaning, please consult the traditional Usage section. You can find some sample configuration +files in the examples/ant directory of the ProGuard distribution. +

+ +

Task Attributes and Nested Elements

+ +The <proguard> task and the +<proguardconfiguration> task can have the following +attributes (only for <proguard>) and nested +elements: + +
+ +
configuration + = "filename"
+
Read and merge options from the given ProGuard-style configuration + file. Note: for reading multiple configuration files or XML-style + configurations, use the configuration + element.
+ +
skipnonpubliclibraryclasses + = "boolean" + (default = false)
+
Ignore non-public library classes.
+ +
skipnonpubliclibraryclassmembers + = "boolean" + (default = true)
+
Ignore package visible library class members.
+ +
target + = "version" + (default = none)
+
Set the given version number in the processed classes.
+ +
forceprocessing + = "boolean" + (default = false)
+
Process the input, even if the output seems up to date.
+ +
printseeds + = "boolean or filename" + (default = false)
+
List classes and class members matched by the various keep + commands, to the standard output or to the given file.
+ +
shrink + = "boolean" + (default = true)
+
Shrink the input class files.
+ +
printusage + = "boolean or filename" + (default = false)
+
List dead code of the input class files, to the standard output or to the + given file.
+ +
optimize + = "boolean" + (default = true)
+
Optimize the input class files.
+ +
optimizationpasses + = "n" + (default = 1)
+
The number of optimization passes to be performed.
+ +
allowaccessmodification + = "boolean" + (default = false)
+
Allow the access modifiers of classes and class members to be modified, + while optimizing.
+ +
mergeinterfacesaggressively + = "boolean" + (default = false)
+
Allow any interfaces to be merged, while optimizing.
+ +
obfuscate + = "boolean" + (default = true)
+
Obfuscate the input class files.
+ +
printmapping + = "boolean or filename" + (default = false)
+
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.
+ +
applymapping + = "filename" + (default = none)
+
Reuse the given mapping, for incremental obfuscation.
+ +
obfuscationdictionary + = "filename" + (default = none)
+
Use the words in the given text file as obfuscated field names and method + names.
+ +
classobfuscationdictionary + = "filename" + (default = none)
+
Use the words in the given text file as obfuscated class names.
+ +
packageobfuscationdictionary + = "filename" + (default = none)
+
Use the words in the given text file as obfuscated package names.
+ +
overloadaggressively + = "boolean" + (default = false)
+
Apply aggressive overloading while obfuscating.
+ +
useuniqueclassmembernames + = "boolean" + (default = false)
+
Ensure uniform obfuscated class member names for subsequent incremental + obfuscation.
+ +
usemixedcaseclassnames + = "boolean" + (default = true)
+
Generate mixed-case class names while obfuscating.
+ +
flattenpackagehierarchy + = "package_name" + (default = none)
+
Repackage all packages that are renamed into the single given parent + package.
+ +
repackageclasses + = "package_name" + (default = none)
+
Repackage all class files that are renamed into the single given + package.
+ +
keepparameternames + = "boolean" + (default = false)
+
Keep the parameter names and types of methods that are kept.
+ +
renamesourcefileattribute + = "string" + (default = none)
+
Put the given constant string in the SourceFile + attributes.
+ +
preverify + = "boolean" + (default = true)
+
Preverify the processed class files if they are targeted at Java Micro + Edition or at Java 6 or higher.
+ +
microedition + = "boolean" + (default = false)
+
Target the processed class files at Java Micro Edition.
+ +
verbose + = "boolean" + (default = false)
+
Write out some more information during processing.
+ +
note + = "boolean" + (default = true)
+
Print notes about potential mistakes or omissions in the configuration. + Use the nested element dontnote for more + fine-grained control.
+ +
warn + = "boolean" + (default = true)
+
Print warnings about unresolved references. Use the nested + element dontwarn for more fine-grained + control. Only use this option if you know what you're doing!
+ +
ignorewarnings + = "boolean" + (default = false)
+
Print warnings about unresolved references, but continue processing + anyhow. Only use this option if you know what you're doing!
+ +
printconfiguration + = "boolean or filename" + (default = false)
+
Write out the entire configuration in traditional ProGuard style, to the + standard output or to the given file. Useful to replace unreadable + XML configurations.
+ +
dump + = "boolean or filename" + (default = false)
+
Write out the internal structure of the processed class files, to the + standard output or to the given file.
+ +
<injar + class_path + />
+
Specifies the program jars (or wars, ears, zips, or directories).
+ +
<outjar + class_path + />
+
Specifies the names of the output jars (or wars, ears, zips, or + directories).
+ +
<libraryjar + class_path + />
+
Specifies the library jars (or wars, ears, zips, or directories).
+ +
<keepdirectory name = "directory_name" + />
+ <keepdirectories filter = "directory_filter" + />
+
Keep the specified directories in the output jars (or wars, ears, zips, or + directories).
+ +
<keep + modifiers + class_specification + > + class_member_specifications + </keep>
+
Preserve the specified classes and class members.
+ +
<keepclassmembers + modifiers + class_specification + > + class_member_specifications + </keepclassmembers>
+
Preserve the specified class members, if their classes are preserved as + well.
+ +
<keepclasseswithmembers + modifiers + class_specification + > + class_member_specifications + </keepclasseswithmembers>
+
Preserve the specified classes and class members, if all of the + specified class members are present.
+ +
<keepnames + class_specification + > + class_member_specifications + </keepnames>
+
Preserve the names of the specified classes and class members (if + they aren't removed in the shrinking step).
+ +
<keepclassmembernames + class_specification + > + class_member_specifications + </keepclassmembernames>
+
Preserve the names of the specified class members (if they aren't removed + in the shrinking step).
+ +
<keepclasseswithmembernames + class_specification + > + class_member_specifications + </keepclasseswithmembernames>
+
Preserve the names of the specified classes and class members, if + all of the specified class members are present (after the shrinking + step).
+ +
<whyareyoukeeping + class_specification + > + class_member_specifications + </whyareyoukeeping>
+
Print details on why the given classes and class members are being kept in + the shrinking step.
+ +
<assumenosideeffects + class_specification + > + class_member_specifications + </assumenosideeffects>
+
Assume that the specified methods don't have any side effects, while + optimizing. Only use this option if you know what you're + doing!
+ +
<optimization name = "optimization_name" + />
+ <optimizations filter = ""optimization_filter" + />
+
Perform only the specified optimizations.
+ +
<keeppackagename name = "package_name" + />
+ <keeppackagenames filter = "package_filter" + />
+
Keep the specified package names from being obfuscated. If no name is + given, all package names are preserved.
+ +
<keepattribute name = "attribute_name" + />
+ <keepattributes filter = "attribute_filter" + />
+
Preserve the specified optional Java bytecode attributes, with optional + wildcards. If no name is given, all attributes are preserved.
+ +
<adaptclassstrings filter = "class_filter" + />
+
Adapt string constants in the specified classes, based on the obfuscated + names of any corresponding classes.
+ +
<adaptresourcefilenames filter = "file_filter" + />
+
Rename the specified resource files, based on the obfuscated names of the + corresponding class files.
+ +
<adaptresourcefilecontents filter = "file_filter" + />
+
Update the contents of the specified resource files, based on the + obfuscated names of the processed classes.
+ +
+ <dontnote filter = "class_filter" + />
+
Don't print notes about classes matching the specified class name + filter.
+ +
+ <dontwarn filter = "class_filter" + />
+
Don't print warnings about classes matching the specified class name + filter. Only use this option if you know what you're doing!
+ +
<configuration refid = "ref_id" + />
+ <configuration file = "name" + />
+
The first form includes the XML-style configuration specified in a + <proguardconfiguration> task (or + <proguard> task) with attribute id = + "ref_id". Only the nested elements of this configuration are + considered, not the attributes. +

+ The second form includes the ProGuard-style configuration from the specified + file. The element is actually a fileset element and supports + all of its attributes and nested elements, including multiple files. +

+ +
+ +

Class Path Attributes and Nested Elements

+ +The jar elements are path elements, so they can have any of the +standard path attributes and nested elements. The most common +attributes are: + +
+ +
path = "path"
+
The names of the jars (or wars, ears, zips, or directories), separated by + the path separator.
+ +
location = "name" (or file + = "name", or dir = "name", or + name = "name")
+
Alternatively, the name of a single jar (or war, ear, zip, or + directory).
+ +
refid = "ref_id"
+
Alternatively, a reference to the path or file set with the attribute + id = "ref_id".
+ +
+ +In addition, the jar elements can have ProGuard-style filter attributes: + +
+ +
filter = + "file_filter"
+
An optional filter for all class file names and resource file names that + are encountered.
+ +
jarfilter = + "file_filter"
+
An optional filter for all jar names that are encountered.
+ +
warfilter = + "file_filter"
+
An optional filter for all war names that are encountered.
+ +
earfilter = + "file_filter"
+
An optional filter for all ear names that are encountered.
+ +
zipfilter = + "file_filter"
+
An optional filter for all zip names that are encountered.
+ +
+ +

Keep Modifier Attributes

+ +The keep tags can have the following modifier attributes: + +
+ +
allowshrinking + = "boolean" + (default = false)
+
Specifies whether the entry points specified in the keep tag may be + shrunk.
+ +
allowoptimization + = "boolean" + (default = false)
+
Specifies whether the entry points specified in the keep tag may be + optimized.
+ +
allowobfuscation + = "boolean" + (default = false)
+
Specifies whether the entry points specified in the keep tag may be + obfuscated.
+ +
+ +

Class Specification Attributes and Nested Elements

+ +The keep tags can have the following class_specification attributes and +class_member_specifications nested elements: + +
+ +
access = "access_modifiers"
+
The optional access modifiers of the class. Any space-separated list of + "public", "final", and "abstract", with optional negators "!".
+ +
annotation = "annotation_name"
+
The optional fully qualified name of an annotation of the class, with + optional wildcards.
+ +
type = "type"
+
The optional type of the class: one of "class", "interface", or + "!interface".
+ +
name = "class_name"
+
The optional fully qualified name of the class, with optional + wildcards.
+ +
extendsannotation = "annotation_name"
+
The optional fully qualified name of an annotation of the the class that + the specified classes must extend, with optional wildcards.
+ +
extends = "class_name"
+
The optional fully qualified name of the class the specified classes + must extend, with optional wildcards.
+ +
implements = "class_name"
+
The optional fully qualified name of the class the specified classes + must implement, with optional wildcards.
+ +
<field + class_member_specification + />
+
Specifies a field.
+ +
<method + class_member_specification + />
+
Specifies a method.
+ +
<constructor + class_member_specification + />
+
Specifies a constructor.
+ +
+ +

Class Member Specification Attributes

+ +The class member tags can have the following class_member_specification +attributes: + +
+ +
access = "access_modifiers"
+
The optional access modifiers of the class. Any space-separated list of + "public", "protected", "private", "static", etc., with optional negators + "!".
+ +
annotation = "annotation_name"
+
The optional fully qualified name of an annotation of the class member, + with optional wildcards.
+ +
type = "type"
+
The optional fully qualified type of the class member, with optional + wildcards. Not applicable for constructors, but required for methods for + which the parameters attribute is specified.
+ +
name = "name"
+
The optional name of the class member, with optional wildcards. Not + applicable for constructors.
+ +
parameters = "parameters"
+
The optional comma-separated list of fully qualified method parameters, + with optional wildcards. Not applicable for fields, but required for + constructors, and for methods for which the type attribute is + specified.
+ +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/attention.gif b/docs/manual/attention.gif new file mode 100644 index 0000000000000000000000000000000000000000..1a0c712d60e41120fa963b4cf988a54f21a69e27 GIT binary patch literal 896 zcmdVX{V&u30LSsqopT;<=dtCIxihsztX7AiWB8&?Y^hW8kcY+Q>6EG8aOdI<$>kwJ z=R{Yei;GK_eF>|uQVC6V&Q`Zk^Dyn0sQ!%Jd;bTo@JPbfFF6Q77$fjE3I$XuOiVzh z!^{j87O=7cg8?QJc6MN~;N}J|FC0G(A0PPpBOm}g9wH+V6$L&YadAjUKvEJ?Q<0X2 z^mJroATtwLS;)>tZY~4@6cj)xgh&Lj7*Z)@GSt+dsR;@N+S<_Gj-DR$_M)#3gM%PR z3=QG+YrJ`b@o`K};@vyU&SGv3^Yd6*g2900Wvs1XeH}(4wzjanjh!9*{*B#T?Ct$e zv$tpzOBBNMpTs{CnC%u73PmO28>M9uX?aEE&8l0~vfDMab@g`|8t*nWx7?GrD%$S1 zcRWx&6r$^qtV`8hrBe4)s-N~rHO~ghw9f~{8L%ICwkQPPHlx626FSD9I0!7tsdT4Bsomj;{0oJ9VPG+*W=mB!1s&nB0-tVp`iE zI5=AzO?}KEJYK+7bIRe$2Kjo`{_(sD4qQi&cFnzOaze zuUSdvQbo1QiR~kMip^YgHJj(0>SCOm3Z5b2W0Zndo5Nq~@&fZ2A?Tz95Ye%L22KuB z9jE!$IKl0%(nP(NEwbFN*2xm~u9K%ta@ + + + + + +ProGuard Examples + + + + +

Examples

+ +Some typical useful configurations: +
    +
  1. A typical application
  2. +
  3. A typical applet
  4. +
  5. A typical midlet
  6. +
  7. A typical Java Card applet
  8. +
  9. A typical xlet
  10. +
  11. A simple Android activity
  12. +
  13. A complete Android application
  14. +
  15. A typical library
  16. +
  17. All possible applications in the input jars
  18. +
  19. All possible applets in the input jars
  20. +
  21. All possible midlets in the input jars
  22. +
  23. All possible Java Card applets in the input jars
  24. +
  25. All possible xlets in the input jars
  26. +
  27. All possible servlets in the input jars
  28. +
  29. Scala applications with the Scala runtime
  30. +
  31. Processing native methods
  32. +
  33. Processing callback methods
  34. +
  35. Processing enumeration classes
  36. +
  37. Processing serializable classes
  38. +
  39. Processing bean classes
  40. +
  41. Processing annotations
  42. +
  43. Processing database drivers
  44. +
  45. Processing ComponentUI classes
  46. +
  47. Processing RMI code
  48. +
  49. Processing resource injection
  50. +
  51. Processing resource files
  52. +
  53. Processing manifest files
  54. +
  55. Producing useful obfuscated stack traces
  56. +
  57. Obfuscating package names
  58. +
  59. Removing logging code
  60. +
  61. Restructuring the output archives
  62. +
  63. Filtering the input and the output
  64. +
  65. Processing multiple applications at once
  66. +
  67. Incremental obfuscation
  68. +
  69. Preverifying class files for Java Micro Edition
  70. +
  71. Upgrading class files to Java 6
  72. +
  73. Finding dead code
  74. +
  75. Printing out the internal structure of class files
  76. +
  77. Using annotations to configure ProGuard
  78. +
+ +You can find some sample configuration files in the examples +directory of the ProGuard distribution. + +

A typical application

+ +To shrink, optimize, and obfuscate a simple Java application, you typically +create a configuration file like myconfig.pro, which can be used +with +
+bin/proguard @myconfig.pro
+
+

+The configuration file specifies the input, the output, and the entry points +of the application: +

+-injars       myapplication.jar
+-outjars      myapplication_out.jar
+-libraryjars  <java.home>/lib/rt.jar
+-printmapping myapplication.map
+
+-keep public class mypackage.MyMain {
+    public static void main(java.lang.String[]);
+}
+
+

+Note the use of the <java.home> system property. ProGuard +automatically replaces it when parsing the file. +

+The -keep option specifies the +entry point of the application that has to be preserved. +The access modifiers public and static 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. +

+Note that all type names are fully specified: +mypackage.MyMain and java.lang.String[]. +

+We're writing out an obfuscation mapping file with -printmapping, for +de-obfuscating any stack traces later on, or for incremental obfuscation of +extensions. +

+We can further improve the results with a few additional options: +

+-optimizationpasses 3
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+
+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 package names. +

+In general, you might need a few additional options for processing native methods, callback methods, +enumerations, serializable +classes, bean classes, annotations, and resource +files. + +

A typical applet

+ +These options shrink, optimize, and obfuscate the applet +mypackage.MyApplet: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+
+-keep public class mypackage.MyApplet
+
+

+The typical applet methods will be preserved automatically, since +mypackage.MyApplet is an extension of the Applet +class in the library rt.jar. +

+If applicable, you should add options for processing native +methods, callback methods, enumerations, serializable +classes, bean classes, annotations, and resource +files. + +

A typical midlet

+ +These options shrink, optimize, obfuscate, and preverify the midlet +mypackage.MyMIDlet: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/wtk2.5.2/lib/midpapi20.jar
+-libraryjars /usr/local/java/wtk2.5.2/lib/cldcapi11.jar
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+-microedition
+
+-keep public class mypackage.MyMIDlet
+
+

+Note how we're now targeting the Java Micro Edition run-time environment of +midpapi20.jar and cldcapi11.jar, instead of the Java +Standard Edition run-time environment rt.jar. You can target +other JME environments by picking the appropriate jars. +

+The typical midlet methods will be preserved automatically, since +mypackage.MyMIDlet is an extension of the MIDlet +class in the library midpapi20.jar. +

+The -microedition option +makes sure the class files are preverified for Java Micro Edition, producing +compact StackMap attributes. It is no longer necessary to run an +external preverifier. +

+Be careful if you do use the external preverify 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 -dontusemixedcaseclassnames +option. +

+If applicable, you should add options for processing native +methods and resource files. +

+Note that you will still have to adapt the midlet jar size in the +corresponding jad file; ProGuard doesn't do that for you. + +

A typical Java Card applet

+ +These options shrink, optimize, and obfuscate the Java Card applet +mypackage.MyApplet: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/javacard2.2.2/lib/api.jar
+-dontwarn    java.lang.Class
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+
+-keep public class mypackage.MyApplet
+
+

+The configuration is very similar to the configuration for midlets, except that +it now targets the Java Card run-time environment. This environment doesn't +have java.lang.Class, so we're telling ProGuard not to worry about it. + +

A typical xlet

+ +These options shrink, optimize, and obfuscate the xlet +mypackage.MyXlet: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/jtv1.1/javatv.jar
+-libraryjars /usr/local/java/cdc1.1/lib/cdc.jar
+-libraryjars /usr/local/java/cdc1.1/lib/btclasses.zip
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+
+-keep public class mypackage.MyXlet
+
+

+The configuration is very similar to the configuration for midlets, except that +it now targets the CDC run-time environment with the Java TV API. + +

A simple Android activity

+ +These options shrink, optimize, and obfuscate the single Android +activity mypackage.MyActivity: +
+-injars      bin/classes
+-outjars     bin/classes-processed.jar
+-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar
+
+-dontpreverify
+-repackageclasses ''
+-allowaccessmodification
+-optimizations !code/simplification/arithmetic
+
+-keep public class mypackage.MyActivity
+
+

+We're targeting the Android run-time and keeping the activity as an entry +point. +

+Preverification is irrelevant for the dex compiler and the Dalvik VM, so we +can switch it off with the +-dontpreverify option. +

+The -optimizations option +disables some arithmetic simplifications that Dalvik 1.0 and 1.5 can't handle. +Note that the Dalvik VM also can't +handle aggressive overloading +(of static fields). +

+If applicable, you should add options for processing native +methods, callback methods, +enumerations, +annotations, and +resource files. + +

A complete Android application

+ +attention +The Ant and Eclipse build processes of the Android SDK already integrate +ProGuard by default, with all the proper settings. You only need to enable +ProGuard (for release builds), by uncommenting the line +"proguard.config=....." in the file +project.properties (created or updated by Android SDK revision 17 +or higher). Notes: +
    +
  • In case of problems, you may want to check if the configuration files that + are listed on this line (proguard-project.txt,...) contain + the necessary settings for your application.
  • +
  • Android SDK revision 20 and higher have a different configuration file for + enabling optimization: + ${sdk.dir}/tools/proguard/proguard-android-optimize.txt + instead of the default + ${sdk.dir}/tools/proguard/proguard-android.txt.
  • +
  • The build processes are already setting the necessary program jars, + library jars, and output jars for you — don't specify them again.
  • +
  • If you get warnings about missing referenced classes: it's all too common + that libraries refer to missing classes. + See "Warning: can't find + referenced class" in the Troubleshooting section.
  • +
+

+For more information, you can consult the official Developer +Guide in the Android SDK. +

+If you're constructing a build process from scratch: these options shrink, +optimize, and obfuscate all public activities, services, broadcast receivers, +and content providers from the compiled classes and external libraries: +

+-injars      bin/classes
+-injars      libs
+-outjars     bin/classes-processed.jar
+-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar
+
+-dontpreverify
+-repackageclasses ''
+-allowaccessmodification
+-optimizations !code/simplification/arithmetic
+-keepattributes *Annotation*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+
+-keep public class * extends android.view.View {
+    public <init>(android.content.Context);
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+    public void set*(...);
+}
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.content.Context {
+   public void *(android.view.View);
+   public void *(android.view.MenuItem);
+}
+
+-keepclassmembers class * implements android.os.Parcelable {
+    static android.os.Parcelable$Creator CREATOR;
+}
+
+-keepclassmembers class **.R$* {
+    public static <fields>;
+}
+
+

+Most importantly, we're keeping all fundamental classes that may be referenced +by the AndroidManifest.xml file of the application. If your +manifest file contains other classes and methods, you may have to specify +those as well. +

+We're keeping annotations, since they might be used by custom +RemoteViews. +

+We're keeping any custom View extensions and other classes with +typical constructors, since they might be referenced from XML layout files. +

+We're also keeping possible onClick handlers in +custom Context extensions, since they might be referenced from +XML layout files. +

+We're also keeping the required static fields in Parcelable +implementations, since they are accessed by introspection. +

+Finally, we're keeping the static fields of referenced inner classes of +auto-generated R classes, just in case your code is accessing +those fields by introspection. Note that the compiler already inlines +primitive fields, so ProGuard can generally remove all these classes entirely +anyway (because the classes are not referenced and therefore not required). +

+If you're using additional Google APIs, you'll have to specify +those as well, for instance: +

+-libraryjars /usr/local/android-sdk/add-ons/google_apis-7_r01/libs/maps.jar
+
+

+If you're using Google's optional License Verification Library, you can +obfuscate its code along with your own code. You do have to preserve +its ILicensingService interface for the library to work: +

+-keep public interface com.android.vending.licensing.ILicensingService
+
+

+If you're using the Android Compatibility library, you should add the +following line, to let ProGuard know it's ok that the library references some +classes that are not available in all versions of the API: +

+-dontwarn android.support.**
+
+

+If applicable, you should add options for processing native +methods, callback methods, +enumerations, +and resource files. You may also want to add +options for producing useful stack traces and +to remove logging. You can find a complete sample +configuration in examples/android.pro in the ProGuard +distribution. + +

A typical library

+ +These options shrink, optimize, and obfuscate an entire library, keeping all +public and protected classes and class members, native method names, and +serialization code. The processed version of the library can then still be +used as such, for developing code based on its public API. +
+-injars       in.jar
+-outjars      out.jar
+-libraryjars  <java.home>/lib/rt.jar
+-printmapping out.map
+
+-keepparameternames
+-renamesourcefileattribute SourceFile
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
+                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
+
+-keep public class * {
+    public protected *;
+}
+
+-keepclassmembernames class * {
+    java.lang.Class class$(java.lang.String);
+    java.lang.Class class$(java.lang.String, boolean);
+}
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private 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();
+}
+
+

+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 -keep options. +

+The -keepclassmembernames +option for the class$ methods is not strictly necessary. These +methods are inserted by the javac compiler and the +jikes compiler respectively, in JDK 1.2 and older, to implement +the .class construct. ProGuard will automatically detect them and +deal with them, even when their names have been obfuscated. However, 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. +

+The "Exceptions" attribute has to be preserved, so the compiler knows which +exceptions methods may throw. +

+The "InnerClasses" attribute (or more precisely, its source name part) has to +be preserved too, for any inner classes that can be referenced from outside the +library. The javac compiler would be unable to find the inner +classes otherwise. +

+The "Signature" attribute is required to be able to access generic types when +compiling in JDK 5.0 and higher. +

+The -keepparameternames +option keeps the parameter names in the "LocalVariableTable" and +"LocalVariableTypeTable" attributes of public library methods. Some IDEs can +present these names to the developers who use the library. +

+Finally, we're keeping the "Deprecated" attribute and the attributes for +producing useful stack traces. +

+We've also added some options for for processing native +methods, enumerations, serializable classes, and annotations, which are all discussed in their +respective examples. + +

All possible applications in the input jars

+ +These options shrink, optimize, and obfuscate all public applications in +in.jar: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+-printseeds
+
+-keepclasseswithmembers public class * {
+    public static void main(java.lang.String[]);
+}
+
+

+Note the use of -keepclasseswithmembers. +We don't want to preserve all classes, just all classes that have main +methods, and those methods. +

+The -printseeds option prints +out which classes exactly will be preserved, so we know for sure we're getting +what we want. +

+If applicable, you should add options for processing native +methods, callback methods, enumerations, serializable +classes, bean classes, annotations, and resource +files. + +

All possible applets in the input jars

+ +These options shrink, optimize, and obfuscate all public applets in +in.jar: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+-printseeds
+
+-keep public class * extends java.applet.Applet
+
+

+We're simply keeping all classes that extend the Applet class. +

+Again, the -printseeds option +prints out which applets exactly will be preserved. +

+If applicable, you should add options for processing native +methods, callback methods, enumerations, serializable +classes, bean classes, annotations, and resource +files. + +

All possible midlets in the input jars

+ +These options shrink, optimize, obfuscate, and preverify all public midlets in +in.jar: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/wtk2.5.2/lib/midpapi20.jar
+-libraryjars /usr/local/java/wtk2.5.2/lib/cldcapi11.jar
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+-microedition
+-printseeds
+
+-keep public class * extends javax.microedition.midlet.MIDlet
+
+

+We're simply keeping all classes that extend the MIDlet class. +

+The -microedition option +makes sure the class files are preverified for Java Micro Edition, producing +compact StackMap attributes. It is no longer necessary to run an +external preverifier. +

+Be careful if you do use the external preverify 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 -dontusemixedcaseclassnames +option. +

+The -printseeds option prints +out which midlets exactly will be preserved. +

+If applicable, you should add options for processing native +methods and resource files. +

+Note that you will still have to adapt the midlet jar size in the +corresponding jad file; ProGuard doesn't do that for you. + +

All possible Java Card applets in the input jars

+ +These options shrink, optimize, and obfuscate all public Java Card applets in +in.jar: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/javacard2.2.2/lib/api.jar
+-dontwarn    java.lang.Class
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+-printseeds
+
+-keep public class * implements javacard.framework.Applet
+
+

+We're simply keeping all classes that implement the Applet +interface. +

+The -printseeds option prints +out which applets exactly will be preserved. + +

All possible xlets in the input jars

+ +These options shrink, optimize, and obfuscate all public xlets in +in.jar: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/jtv1.1/javatv.jar
+-libraryjars /usr/local/java/cdc1.1/lib/cdc.jar
+-libraryjars /usr/local/java/cdc1.1/lib/btclasses.zip
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+-printseeds
+
+-keep public class * implements javax.tv.xlet.Xlet
+
+

+We're simply keeping all classes that implement the Xlet interface. +

+The -printseeds option prints +out which xlets exactly will be preserved. + +

All possible servlets in the input jars

+ +These options shrink, optimize, and obfuscate all public servlets in +in.jar: +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+-libraryjars /usr/local/java/servlet/servlet.jar
+-printseeds
+
+-keep public class * implements javax.servlet.Servlet
+
+

+Keeping all servlets is very similar to keeping all applets. The servlet API +is not part of the standard run-time jar, so we're specifying it as a library. +Don't forget to use the right path name. +

+We're then keeping all classes that implement the Servlet +interface. We're using the implements keyword because it looks +more familiar in this context, but it is equivalent to extends, +as far as ProGuard is concerned. +

+The -printseeds option prints +out which servlets exactly will be preserved. +

+If applicable, you should add options for processing native +methods, callback methods, enumerations, serializable +classes, bean classes, annotations, and resource +files. + +

Scala applications with the Scala runtime

+ +These options shrink, optimize, and obfuscate all public Scala applications in +in.jar: +
+-injars      in.jar
+-injars      /usr/local/java/scala-2.9.1/lib/scala-library.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+
+-dontwarn scala.**
+
+-keepclasseswithmembers public class * {
+    public static void main(java.lang.String[]);
+}
+
+-keep class * implements org.xml.sax.EntityResolver
+
+-keepclassmembers class * {
+    ** MODULE$;
+}
+
+-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinPool {
+    long eventCount;
+    int  workerCounts;
+    int  runControl;
+    scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode syncStack;
+    scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode spareStack;
+}
+
+-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinWorkerThread {
+    int base;
+    int sp;
+    int runState;
+}
+
+-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinTask {
+    int status;
+}
+
+-keepclassmembernames class scala.concurrent.forkjoin.LinkedTransferQueue {
+    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference head;
+    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference tail;
+    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference cleanMe;
+}
+
+

+The configuration is essentially the same as +for processing applications, because Scala is +compiled to ordinary Java bytecode. However, the example processes the Scala +runtime library as well. The processed jar can be an order of magnitude +smaller and a few times faster than the original code (for the Scala code +examples, for instance). +

+The -dontwarn option tells +ProGuard not to complain about some artefacts in the Scala runtime, the way it +is compiled by the scalac compiler (at least in Scala 2.9.1 and +older). Note that this option should always be used with care. +

+The additional -keep +options make sure that some classes and some fields that are accessed by means +of introspection are not removed or renamed. +

+If applicable, you should add options for processing native +methods, callback methods, enumerations, serializable +classes, bean classes, annotations, and resource +files. +

Processing native methods

+ +If your application, applet, servlet, library, etc., contains native methods, +you'll want to preserve their names and their classes' names, so they can +still be linked to the native library. The following additional option will +ensure that: +
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+

+Note the use of -keepclasseswithmembernames. +We don't want to preserve all classes or all native methods; we just want to +keep the relevant names from being obfuscated. +

+ProGuard doesn't look at your native code, so it won't automatically preserve +the classes or class members that are invoked by the native code. These are +entry points, which you'll have to specify explicitly. Callback methods are discussed below as a typical example. + +

Processing callback methods

+ +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 -keep options, something like +the following option will keep the callback class and method: +
+-keep class mypackage.MyCallbackClass {
+    void myCallbackMethod(java.lang.String);
+}
+
+

+This will preserve the given class and method from being removed or renamed. + +

Processing enumeration classes

+ +If your application, applet, servlet, library, etc., contains enumeration +classes, you'll have to preserve some special methods. Enumerations were +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 +generation). You have to specify these explicitly, to make sure they aren't +removed or obfuscated: +
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+ +

Processing serializable classes

+ +More complex applications, applets, servlets, libraries, etc., may contain +classes that are serialized. Depending on the way in which they are used, they +may require special attention: +
    + +
  • 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 options: + +
    +-keepclassmembers class * implements java.io.Serializable {
    +    private 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();
    +}
    +
    +

    + + The -keepclassmembers + option makes sure that any serialization methods are kept. By using this + option instead of the basic -keep option, we're not + forcing preservation of all serializable classes, just preservation + of the listed members of classes that are actually used.

  • + +
  • Sometimes, the serialized data are stored, and read back later into newer + 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 serialVersionUID fields. The following options should + then be sufficient to ensure compatibility over time: + +
    +-keepnames class * implements java.io.Serializable
    +
    +-keepclassmembers class * implements java.io.Serializable {
    +    static final long serialVersionUID;
    +    private static final java.io.ObjectStreamField[] serialPersistentFields;
    +    !static !transient <fields>;
    +    private void writeObject(java.io.ObjectOutputStream);
    +    private void readObject(java.io.ObjectInputStream);
    +    java.lang.Object writeReplace();
    +    java.lang.Object readResolve();
    +}
    +
    +

    + + The serialVersionUID and serialPersistentFields + lines makes sure those fields are preserved, if they are present. + The <fields> line preserves all non-static, + non-transient fields, with their original names. The introspection of the + serialization process and the de-serialization process will then find + consistent names.

  • + +
  • Occasionally, the serialized data have to remain compatible, but the + classes involved lack serialVersionUID fields. I imagine the + original code will then be hard to maintain, since the serial version UID + is then computed from a list of features the serializable class. Changing + the class ever so slightly may change the computed serial version UID. The + list of features is specified in the section on Stream + Unique Identifiers of Sun's Java + Object Serialization Specification. The following directives should at + least partially ensure compatibility with the original classes: + +
    +-keepnames class * implements java.io.Serializable
    +
    +-keepclassmembers class * implements java.io.Serializable {
    +    static final long serialVersionUID;
    +    private static final java.io.ObjectStreamField[] serialPersistentFields;
    +    !static !transient <fields>;
    +    !private <fields>;
    +    !private <methods>;
    +    private void writeObject(java.io.ObjectOutputStream);
    +    private void readObject(java.io.ObjectInputStream);
    +    java.lang.Object writeReplace();
    +    java.lang.Object readResolve();
    +}
    +
    +

    + + 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 "-keep + interface MyInterface"), since these names are also used when + computing the UID. A fast but sub-optimal alternative would be simply + keeping all interfaces with "-keep interface *".

  • + +
+

+ +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 Serialization interface, yet only a small number may actually +ever be serialized. Knowing your application and tuning the configuration +often produces more compact results. + +

Processing bean classes

+ +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 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: +
+-keep public class mypackage.MyBean {
+    public void setMyProperty(int);
+    public int getMyProperty();
+}
+
+-keep public class mypackage.MyBeanEditor
+
+

+If there are too many elements to list explicitly, wildcards in class names +and method signatures might be helpful. This example preserves all possible +setters and getters in classes in the package mybeans: +

+-keep class mybeans.** {
+    void set*(***);
+    void set*(int, ***);
+
+    boolean is*(); 
+    boolean is*(int);
+
+    *** get*();
+    *** get*(int);
+}
+
+

+The '***' wildcard matches any type (primitive or non-primitive, +array or non-array). The methods with the 'int' arguments matches +properties that are lists. + +

Processing annotations

+ +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: +
+-keepattributes *Annotation*
+
+

+For brevity, we're specifying a wildcarded attribute name, which will match +RuntimeVisibleAnnotations, +RuntimeInvisibleAnnotations, +RuntimeVisibleParameterAnnotations, +RuntimeInvisibleParameterAnnotations, and +AnnotationDefault. Depending on the purpose of the processed +code, you could refine this selection, for instance not keeping the run-time +invisible annotations (which are only used at compile-time). +

+Some code may make further use of introspection to figure out the enclosing +methods of anonymous inner classes. In that case, the corresponding attribute +has to be preserved as well: +

+-keepattributes EnclosingMethod
+
+ +

Processing database drivers

+ +Database drivers are implementations of the Driver interface. +Since they are often created dynamically, you may want to preserve any +implementations that you are processing as entry points: +
+-keep class * implements java.sql.Driver
+
+

+This option also gets rid of the note that ProGuard prints out about +(java.sql.Driver)Class.forName constructs, if you are +instantiating a driver in your code (without necessarily implementing any +drivers yourself). + +

Processing ComponentUI classes

+ +Swing UI look and feels are implemented as extensions of the +ComponentUI class. For some reason, these have to contain a +static method createUI, which the Swing API invokes using +introspection. You should therefore always preserve the method as an entry +point, for instance like this: +
+-keep class * extends javax.swing.plaf.ComponentUI {
+    public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent);
+}
+
+

+This option also keeps the classes themselves. + +

Processing RMI code

+ +Reportedly, the easiest way to handle RMI code is to process the code with +ProGuard first and then invoke the rmic tool. If that is not +possible, you may want to try something like this: +
+-keepattributes Exceptions
+
+-keep interface * extends java.rmi.Remote {
+    <methods>;
+}
+
+-keep class * implements java.rmi.Remote {
+    <init>(java.rmi.activation.ActivationID, java.rmi.MarshalledObject);
+}
+
+

+The first -keep option keeps all your Remote interfaces and their +methods. The second one keeps all the implementations, along with their +particular RMI constructors, if any. +

+The Exceptions attribute has to be kept too, because the RMI +handling code performs introspection to check whether the method signatures +are compatible. + +

Processing resource injection

+ +If your application is using JEE-style resource injection, the application +container will automatically assign instances of resource classes to fields and +methods that are annotated with @Resource. The container applies +introspection, even accessing private class members directly. It typically +constructs a resource name based on the type name and the class member name. +We then have to avoid that such class members are removed or renamed: +
+-keepclassmembers class * {
+    @javax.annotation.Resource *;
+}
+
+

+The Spring framework has another similar annotation @Autowired: +

+-keepclassmembers class * {
+    @org.springframework.beans.factory.annotation.Autowired *;
+}
+
+ +

Processing resource files

+ +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: +
+-adaptresourcefilenames    **.properties,**.gif,**.jpg
+-adaptresourcefilecontents **.properties,META-INF/MANIFEST.MF
+
+

+The -adaptresourcefilenames +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 -adaptresourcefilecontents +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. + +

Processing manifest files

+ +As illustrated in the previous section, manifest files can be treated like +ordinary resource files. ProGuard can adapt obfuscated class names in the +files, but it won't make any other changes. If you want anything else, you +should apply an external tool. For instance, if a manifest file contains +signing information, you should sign the jar again after it has been +processed. +

+If you're merging several input jars into a single output jar, you'll have to +pick one, typically by specifying filters: +

+-injars  in1.jar
+-injars  in2.jar(!META-INF/MANIFEST.MF)
+-injars  in3.jar(!META-INF/MANIFEST.MF)
+-outjars out.jar
+
+

+The filters will let ProGuard copy the manifest file from the first jar and +ignore any manifest files in the second and third input jars. Note that +ProGuard will leave the order of the files in the jars unchanged; manifest +files are not necessarily put first. + +

Producing useful obfuscated stack traces

+ +These options let obfuscated applications or libraries produce stack traces +that can still be deciphered later on: +
+-printmapping out.map
+
+-renamesourcefileattribute SourceFile
+-keepattributes SourceFile,LineNumberTable
+
+

+We're keeping all source file attributes, but we're replacing their values by +the string "SourceFile". We could use any string. This string is already +present in all class files, so it doesn't take up any extra space. If you're +working with J++, you'll want to keep the "SourceDir" attribute as well. +

+We're also keeping the line number tables of all methods. +

+Whenever both of these attributes are present, the Java run-time environment +will include line number information when printing out exception stack traces. +

+The information will only be useful if we can map the obfuscated names back to +their original names, so we're saving the mapping to a file +out.map. The information can then be used by the ReTrace tool to restore the original stack trace. + +

Obfuscating package names

+ +Package names can be obfuscated in various ways, with increasing levels of +obfuscation and compactness. For example, consider the following classes: +
+mycompany.myapplication.MyMain
+mycompany.myapplication.Foo
+mycompany.myapplication.Bar
+mycompany.myapplication.extra.FirstExtra
+mycompany.myapplication.extra.SecondExtra
+mycompany.util.FirstUtil
+mycompany.util.SecondUtil
+
+

+Let's assume the class name mycompany.myapplication.MyMain is the +main application class that is kept by the configuration. All other class names +can be obfuscated. +

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

+mycompany.myapplication.MyMain
+mycompany.myapplication.a
+mycompany.myapplication.b
+mycompany.myapplication.a.a
+mycompany.myapplication.a.b
+mycompany.a.a
+mycompany.a.b
+
+

+The -flattenpackagehierarchy +option obfuscates the package names further, by flattening the package +hierarchy of obfuscated packages: +

+-flattenpackagehierarchy 'myobfuscated'
+
+

+The obfuscated class names then look as follows: +

+mycompany.myapplication.MyMain
+mycompany.myapplication.a
+mycompany.myapplication.b
+myobfuscated.a.a
+myobfuscated.a.b
+myobfuscated.b.a
+myobfuscated.b.b
+
+

+Alternatively, the -repackageclasses option +obfuscates the entire packaging, by combining obfuscated classes into a single +package: +

+-repackageclasses 'myobfuscated'
+
+The obfuscated class names then look as follows: +
+mycompany.myapplication.MyMain
+mycompany.myapplication.a
+mycompany.myapplication.b
+myobfuscated.a
+myobfuscated.b
+myobfuscated.c
+myobfuscated.d
+
+

+Additionally specifying the -allowaccessmodification +option allows access permissions of classes and class members to +be broadened, opening up the opportunity to repackage all obfuscated classes: +

+-repackageclasses 'myobfuscated'
+-allowaccessmodification
+
+The obfuscated class names then look as follows: +
+mycompany.myapplication.MyMain
+myobfuscated.a
+myobfuscated.b
+myobfuscated.c
+myobfuscated.d
+myobfuscated.e
+myobfuscated.f
+
+

+The specified target package can always be the root package. For instance: +

+-repackageclasses ''
+-allowaccessmodification
+
+The obfuscated class names are then the shortest possible names: +
+mycompany.myapplication.MyMain
+a
+b
+c
+d
+e
+f
+
+

+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 resource files that have to be adapted. + +

Removing logging code

+ +You can let ProGuard remove logging code. The trick is to specify that the +logging methods don't have side-effects — even though they actually do, +since they write to the console or to a log file. ProGuard will take your word +for it and remove the invocations (in the optimization step) and if possible +the logging classes and methods themselves (in the shrinking step). +

+For example, this configuration removes invocations of the Android logging +methods: +

+-assumenosideeffects class android.util.Log {
+    public static boolean isLoggable(java.lang.String, int);
+    public static int v(...);
+    public static int i(...);
+    public static int w(...);
+    public static int d(...);
+    public static int e(...);
+}
+
+

+The wildcards are a shortcut to match all versions of the methods. +

+Note that you generally can't remove logging code that uses +System.out.println, since you would be removing all invocations +of java.io.PrintStream#println, which could break your +application. You can work around it by creating your own logging methods and +let ProGuard remove those. + +

Restructuring the output archives

+ +In simple applications, all output classes and resources files are merged into +a single jar. For example: +
+-injars  classes
+-injars  in1.jar
+-injars  in2.jar
+-injars  in3.jar
+-outjars out.jar
+
+

+This configuration merges the processed versions of the files in the +classes directory and the three jars into a single output jar +out.jar. +

+If you want to preserve the structure of your input jars (and/or wars, ears, +zips, or directories), you can specify an output directory (or a war, an ear, +or a zip). For example: +

+-injars  in1.jar
+-injars  in2.jar
+-injars  in3.jar
+-outjars out
+
+

+The input jars will then be reconstructed in the directory out, +with their original names. +

+You can also combine archives into higher level archives. For example: +

+-injars  in1.jar
+-injars  in2.jar
+-injars  in3.jar
+-outjars out.war
+
+

+The other way around, you can flatten the archives inside higher level +archives into simple archives: +

+-injars  in.war
+-outjars out.jar
+
+

+This configuration puts the processed contents of all jars inside +in.war (plus any other contents of in.war) into +out.jar. +

+If you want to combine input jars (and/or wars, ears, zips, or directories) +into output jars (and/or wars, ears, zips, or directories), you can group the +-injars and -outjars options. For example: +

+-injars base_in1.jar
+-injars base_in2.jar
+-injars base_in3.jar
+-outjars base_out.jar
+
+-injars  extra_in.jar
+-outjars extra_out.jar
+
+

+This configuration puts the processed results of all base_in*.jar +jars into base_out.jar, and the processed results of the +extra_in.jar into extra_out.jar. Note that only the +order of the options matters; the additional whitespace is just for clarity. +

+This grouping, archiving, and flattening can be arbitrarily complex. ProGuard +always tries to package output archives in a sensible way, reconstructing the +input entries as much as required. + +

Filtering the input and the output

+ +If you want even greater control, you can add +filters to the input and the output, +filtering out zips, ears, wars, jars, and/or ordinary files. For example, if +you want to disregard certain files from an input jar: +
+-injars  in.jar(!images/**)
+-outjars out.jar
+
+

+This configuration removes any files in the images directory and +its subdirectories. +

+Such filters can be convenient for avoiding warnings about duplicate files in +the output. For example, only keeping the manifest file from a first input jar: +

+-injars  in1.jar
+-injars  in2.jar(!META-INF/MANIFEST.MF)
+-injars  in3.jar(!META-INF/MANIFEST.MF)
+-outjars out.jar
+
+

+Another useful application is speeding up the processing by ProGuard, by +disregarding a large number of irrelevant classes in the runtime library jar: +

+-libraryjars <java.home>/lib/rt.jar(java/**,javax/**)
+
+

+The filter makes ProGuard disregard com.sun.** classes, for +instance , which don't affect the processing of ordinary applications. +

+It is also possible to filter the jars (and/or wars, ears, zips) themselves, +based on their names. For example: +

+-injars  in(**/acme_*.jar;)
+-outjars out.jar
+
+

+Note the semi-colon in the filter; the filter in front of it applies to jar +names. In this case, only acme_*.jar jars are read from the +directory in and its subdirectories. Filters for war names, ear +names, and zip names can be prefixed with additional semi-colons. All types of +filters can be combined. They are orthogonal. +

+On the other hand, you can also filter the output, in order to control what +content goes where. For example: +

+-injars  in.jar
+-outjars code_out.jar(**.class)
+-outjars resources_out.jar
+
+

+This configuration splits the processed output, sending **.class +files to code_out.jar, and all remaining files to +resources_out.jar. +

+Again, the filtering can be arbitrarily complex, especially when combined with +grouping input and output. + +

Processing multiple applications at once

+ +You can process several dependent or independent applications (or applets, +midlets,...) in one go, in order to save time and effort. ProGuard's input and +output handling offers various ways to keep the output nicely structured. +

+The easiest way is to specify your input jars (and/or wars, ears, zips, and +directories) and a single output directory. ProGuard will then reconstruct the +input in this directory, using the original jar names. For example, showing +just the input and output options: +

+-injars  application1.jar
+-injars  application2.jar
+-injars  application3.jar
+-outjars processed_applications
+
+

+After processing, the directory processed_applications will +contain processed versions of application jars, with their original names. + +

Incremental obfuscation

+ +After having processed an application, e.g. +ProGuard itself, you can still incrementally add other pieces of code that +depend on it, e.g. the ProGuard GUI: +
+-injars       proguardgui.jar
+-outjars      proguardgui_out.jar
+-injars       proguard.jar
+-outjars      proguard_out.jar
+-libraryjars  <java.home>/lib/rt.jar
+-applymapping proguard.map
+
+-keep public class proguard.gui.ProGuardGUI {
+    public static void main(java.lang.String[]);
+}
+
+

+We're reading both unprocessed jars as input. Their processed contents will go +to the respective output jars. The -applymapping option then +makes sure the ProGuard part of the code gets the previously produced +obfuscation mapping. The final application will consist of the obfuscated +ProGuard jar and the additional obfuscated GUI jar. +

+The added code in this example is straightforward; it doesn't affect the +original code. The proguard_out.jar will be identical to the one +produced in the initial processing step. If you foresee adding more complex +extensions to your code, you should specify the options -useuniqueclassmembernames, +-dontshrink, and -dontoptimize in the +original processing step. These options ensure that the obfuscated base +jar will always remain usable without changes. You can then specify the base +jar as a library jar: +

+-injars       proguardgui.jar
+-outjars      proguardgui_out.jar
+-libraryjars  proguard.jar
+-libraryjars  <java.home>/lib/rt.jar
+-applymapping proguard.map
+
+-keep public class proguard.gui.ProGuardGUI {
+    public static void main(java.lang.String[]);
+}
+
+ +

Preverifying class files for Java Micro Edition

+ +Even if you're not interested in shrinking, optimizing, and obfuscating your +midlets, as shown in the midlets example, you can still +use ProGuard to preverify the class files for Java Micro Edition. ProGuard +produces slightly more compact results than the traditional external +preverifier. +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/wtk2.5.2/lib/midpapi20.jar
+-libraryjars /usr/local/java/wtk2.5.2/lib/cldcapi11.jar
+
+-dontshrink
+-dontoptimize
+-dontobfuscate
+
+-microedition
+
+

+We're not processing the input, just making sure the class files are +preverified by targeting them at Java Micro Edition with the -microedition option. Note +that we don't need any -keep options to specify entry points; all +class files are simply preverified. + +

Upgrading class files to Java 6

+ +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. +
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+
+-dontshrink
+-dontoptimize
+-dontobfuscate
+
+-target 1.6
+
+

+We're not processing the input, just retargeting the class files with the -target option. They will +automatically be preverified for Java 6 as a result. Note that we don't need +any -keep options to specify entry points; all class files are +simply updated and preverified. + +

Finding dead code

+ +These options list unused classes, fields, and methods in the application +mypackage.MyApplication: +
+-injars      in.jar
+-libraryjars <java.home>/lib/rt.jar
+
+-dontoptimize
+-dontobfuscate
+-dontpreverify
+-printusage
+
+-keep public class mypackage.MyApplication {
+    public static void main(java.lang.String[]);
+}
+
+

+We're not specifying an output jar, just printing out some results. We're +saving some processing time by skipping the other processing steps. +

+The java compiler inlines primitive constants and String constants +(static final fields). ProGuard would therefore list such fields +as not being used in the class files that it analyzes, even if they are +used in the source files. We can add a -keepclassmembers option +that keeps those fields a priori, in order to avoid having them listed: +

+-keepclassmembers class * {
+    static final %                *;
+    static final java.lang.String *;
+}
+
+ +

Printing out the internal structure of class files

+ +These options print out the internal structure of all class files in the input +jar: +
+-injars in.jar
+
+-dontshrink
+-dontoptimize
+-dontobfuscate
+-dontpreverify
+
+-dump
+
+

+Note how we don't need to specify the Java run-time jar, because we're not +processing the input jar at all. + +

Using annotations to configure ProGuard

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

+You can find a set of such predefined annotations in the directory +examples/annotations/lib in the ProGuard distribution. +The annotation classes are defined in annotations.jar. The +corresponding ProGuard configuration (or meta-configuration, if you prefer) +is specified in annotations.pro. With these files, you can start +annotating your code. For instance, a java source file +Application.java can be annotated as follows: +

+@KeepApplication
+public class Application {
+  ....
+}
+
+

+The ProGuard configuration file for the application can then be simplified by +leveraging off these annotations: +

+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+
+-include lib/annotations.pro
+
+

+The annotations are effectively replacing the application-dependent +-keep options. You may still wish to add traditional +-keep options for processing native +methods, enumerations, serializable classes, and annotations. +

+The directory examples/annotations contains more examples that +illustrate some of the possibilities. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/gradle.html b/docs/manual/gradle.html new file mode 100644 index 000000000..ef7280d36 --- /dev/null +++ b/docs/manual/gradle.html @@ -0,0 +1,545 @@ + + + + + + +Gradle Task + + + + +

Gradle Task

+ +ProGuard can be run as a task in the Java-based build tool Gradle +(version 1.3 or higher). +

+ +Before you can use the proguard task, you have to make sure +Gradle can find it in its class path at build time. One way is to add the +following line to your build.gradle file: +

+ +

+buildscript {
+    repositories {
+        flatDir dirs: '/usr/local/java/proguard/lib'
+    }
+    dependencies {
+        classpath ':proguard'
+    }
+}
+
+

+ +Please make sure the class path is set correctly for your system. +

+ +You can then define a task: +

+

+task myProguardTask(type: proguard.gradle.ProGuardTask) {
+    .....
+}
+
+

+ +The embedded configuration is much like a standard ProGuard configuration. +Notable similarities and differences: +

    +
  • Like in ProGuard-style configurations, we're using all lower-case names + for the settings.
  • +
  • The options don't have a dash as prefix.
  • +
  • Arguments typically have quotes.
  • +
  • Some settings are specified as named arguments.
  • +
+

+You can find some sample build files in the examples/gradle +directory of the ProGuard distribution. +

+If you prefer a more verbose configuration derived from the Ant task, you can +import the Ant task as a Gradle task. + +

Settings

+ +The ProGuard task supports the following settings in its closure: + +
+ +
configuration + file
+
Read and merge options from the given ProGuard-style configuration + file.
+ +
injars + class_path
+
Specifies the program jars (or wars, ears, zips, or directories).
+ +
outjars + class_path
+
Specifies the names of the output jars (or wars, ears, zips, or + directories).
+ +
libraryjars + class_path
+
Specifies the library jars (or wars, ears, zips, or directories).
+ +
skipnonpubliclibraryclasses
+
Ignore non-public library classes.
+ +
dontskipnonpubliclibraryclassmembers
+
Don't ignore package visible library class members.
+ +
keepdirectories + ['directory_filter']
+
Keep the specified directories in the output jars (or wars, ears, zips, + or directories).
+ +
target + 'version'
+
Set the given version number in the processed classes.
+ +
forceprocessing
+
Process the input, even if the output seems up to date.
+ +
keep + [modifier,...] + class_specification
+
Preserve the specified classes and class members.
+ +
keepclassmembers + [modifier,...] + class_specification
+
Preserve the specified class members, if their classes are preserved as + well.
+ +
keepclasseswithmembers + [modifier,...] + class_specification
+
Preserve the specified classes and class members, if all of the + specified class members are present.
+ +
keepnames + class_specification
+
Preserve the names of the specified classes and class members (if + they aren't removed in the shrinking step).
+ +
keepclassmembernames + class_specification
+
Preserve the names of the specified class members (if they aren't removed + in the shrinking step).
+ +
keepclasseswithmembernames + class_specification
+
Preserve the names of the specified classes and class members, if + all of the specified class members are present (after the shrinking + step).
+ +
printseeds + [file]
+
List classes and class members matched by the various keep + commands, to the standard output or to the given file.
+ +
dontshrink
+
Don't shrink the input class files.
+ +
printusage + [file]
+
List dead code of the input class files, to the standard output or to the + given file.
+ +
whyareyoukeeping + class_specification
+
Print details on why the given classes and class members are being kept in + the shrinking step.
+ +
dontoptimize
+
Don't optimize the input class files.
+ +
optimizations 'optimization_filter'
+
Perform only the specified optimizations.
+ +
optimizationpasses + n
+
The number of optimization passes to be performed.
+ +
assumenosideeffects + class_specification
+
Assume that the specified methods don't have any side effects, while + optimizing. Only use this option if you know what you're + doing!
+ +
allowaccessmodification
+
Allow the access modifiers of classes and class members to be modified, + while optimizing.
+ +
mergeinterfacesaggressively
+
Allow any interfaces to be merged, while optimizing.
+ +
dontobfuscate
+
Don't obfuscate the input class files.
+ +
printmapping + [file]
+
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.
+ +
applymapping + file
+
Reuse the given mapping, for incremental obfuscation.
+ +
obfuscationdictionary + file
+
Use the words in the given text file as obfuscated field names and method + names.
+ +
classobfuscationdictionary + file
+
Use the words in the given text file as obfuscated class names.
+ +
packageobfuscationdictionary + file
+
Use the words in the given text file as obfuscated package names.
+ +
overloadaggressively
+
Apply aggressive overloading while obfuscating.
+ +
useuniqueclassmembernames
+
Ensure uniform obfuscated class member names for subsequent incremental + obfuscation.
+ +
dontusemixedcaseclassnames
+
Don't generate mixed-case class names while obfuscating.
+ +
keeppackagenames ['package_filter']
+
Keep the specified package names from being obfuscated. If no name is + given, all package names are preserved.
+ +
flattenpackagehierarchy + 'package_name'
+
Repackage all packages that are renamed into the single given parent + package.
+ +
repackageclasses + ['package_name']
+
Repackage all class files that are renamed into the single given + package.
+ +
keepattributes ['attribute_filter']
+
Preserve the specified optional Java bytecode attributes, with optional + wildcards. If no name is given, all attributes are preserved.
+ +
keepparameternames
+
Keep the parameter names and types of methods that are kept.
+ +
renamesourcefileattribute + ['string']
+
Put the given constant string in the SourceFile + attributes.
+ +
adaptclassstrings + ['class_filter']
+
Adapt string constants in the specified classes, based on the obfuscated + names of any corresponding classes.
+ +
adaptresourcefilenames + ['file_filter']
+
Rename the specified resource files, based on the obfuscated names of the + corresponding class files.
+ +
adaptresourcefilecontents + ['file_filter']
+
Update the contents of the specified resource files, based on the + obfuscated names of the processed classes.
+ +
dontpreverify
+
Don't preverify the processed class files if they are targeted at Java Micro + Edition or at Java 6 or higher.
+ +
microedition
+
Target the processed class files at Java Micro Edition.
+ +
verbose
+
Write out some more information during processing.
+ +
dontnote 'class_filter'
+
Don't print notes about classes matching the specified class name + filter.
+ +
dontwarn 'class_filter'
+
Don't print warnings about classes matching the specified class name + filter. Only use this option if you know what you're doing!
+ +
ignorewarnings
+
Print warnings about unresolved references, but continue processing + anyhow. Only use this option if you know what you're doing!
+ +
printconfiguration + [file]
+
Write out the entire configuration in traditional ProGuard style, to the + standard output or to the given file. Useful to replace unreadable + XML configurations.
+ +
dump + [file]
+
Write out the internal structure of the processed class files, to the + standard output or to the given file.
+ +
+ +

Class Paths

+ +Class paths are specified as Gradle file collections, which means they can be +specified as simple strings, with files(Object), etc. +

+In addition, they can have ProGuard-style filters, specified as +comma-separated named arguments after the file: + +

+ +
filter: + 'file_filter'
+
An optional filter for all class file names and resource file names that + are encountered.
+ +
jarfilter: + 'file_filter'
+
An optional filter for all jar names that are encountered.
+ +
warfilter: + 'file_filter'
+
An optional filter for all war names that are encountered.
+ +
earfilter: + 'file_filter'
+
An optional filter for all ear names that are encountered.
+ +
zipfilter: + 'file_filter'
+
An optional filter for all zip names that are encountered.
+ +
+ +

Files

+ +Files are specified as Gradle files, which means they can be specified +as simple strings, as File instances, with file(Object), etc. +

+In Gradle, file names (any strings really) in double quotes can contain +properties or code inside ${...}. These are automatically +expanded. +

+Like in ProGuard-style configurations, the names can also contain Java system +properties, delimited by angular brackets, '<' and '>'. +These properties are automatically replaced by their corresponding values. +

+For example, <java.home>/lib/rt.jar is automatically +expanded to something like /usr/local/java/jdk/jre/lib/rt.jar. +Similarly, <user.home> is expanded to the user's home +directory, and <user.dir> is expanded to the current +working directory. + +

Keep Modifiers

+ +The keep settings can have the following named arguments that modify their +behaviors: + +
+ +
allowshrinking: + boolean + (default = false)
+
Specifies whether the entry points specified in the keep tag may be + shrunk.
+ +
allowoptimization: + boolean + (default = false)
+
Specifies whether the entry points specified in the keep tag may be + optimized.
+ +
allowobfuscation: + boolean + (default = false)
+
Specifies whether the entry points specified in the keep tag may be + obfuscated.
+ +
+ +Names arguments are comma-separated, as usual. + +

Class Specifications

+ +A class specification is a template of classes and class members (fields and methods). There are two alternative ways to specify such a template: + +
    +
  1. As a string containing a ProGuard-style class specification. This is the + most compact and most readable way. The specification looks like a Java + declaration of a class with fields and methods. For example: +
    +keep 'public class mypackage.MyMainClass {  \
    +    public static void main(java.lang.String[]);  \
    +}'
    +
  2. +
  3. As a Gradle-style setting: a method calls with named arguments and a + closure. This is more verbose, but it might be useful for programmatic + specifications. For example: +
    +keep access: 'public',
    +     name:   'mypackage.MyMainClass', {
    +    method access:     'public static',
    +           type:       'void',
    +           name:       'main',
    +           parameters: 'java.lang.String[]'
    +}
    +
  4. +
+

+ +The ProGuard-style class +specification is described on the traditional Usage page. +

+A Gradle-style class specification can have the following named arguments: + +

+ +
access: 'access_modifiers'
+
The optional access modifiers of the class. Any space-separated list of + "public", "final", and "abstract", with optional negators "!".
+ +
annotation: 'annotation_name'
+
The optional fully qualified name of an annotation of the class, with + optional wildcards.
+ +
type: 'type'
+
The optional type of the class: one of "class", "interface", or + "!interface".
+ +
name: 'class_name'
+
The optional fully qualified name of the class, with optional + wildcards.
+ +
extendsannotation: 'annotation_name'
+
The optional fully qualified name of an annotation of the the class that + the specified classes must extend, with optional wildcards.
+ +
'extends': 'class_name'
+
The optional fully qualified name of the class the specified classes + must extend, with optional wildcards.
+ +
'implements': 'class_name'
+
The optional fully qualified name of the class the specified classes + must implement, with optional wildcards.
+ +
+ +The named arguments are optional. Without any arguments, there are no +constraints, so the settings match all classes. +

+ +

Gradle-style Class Member Specifications

+ +The closure of a Gradle-style class specification can specify class members +with these settings: + +
+ +
field field_constraints
+
Specifies a field.
+ +
method method_constraints
+
Specifies a method.
+ +
constructor constructor_constraints
+
Specifies a constructor.
+ +
+ +A class member setting can have the following named arguments to express +constraints: + +
+ +
access: 'access_modifiers'
+
The optional access modifiers of the class. Any space-separated list of + "public", "protected", "private", "static", etc., with optional negators + "!".
+ +
'annotation': 'annotation_name'
+
The optional fully qualified name of an annotation of the class member, + with optional wildcards.
+ +
type: 'type'
+
The optional fully qualified type of the class member, with optional + wildcards. Not applicable for constructors, but required for methods for + which the parameters argument is specified.
+ +
name: 'name'
+
The optional name of the class member, with optional wildcards. Not + applicable for constructors.
+ +
parameters: 'parameters'
+
The optional comma-separated list of fully qualified method parameters, + with optional wildcards. Not applicable for fields, but required for + constructors, and for methods for which the type argument is + specified.
+ +
+ +The named arguments are optional. Without any arguments, there are no +constraints, so the settings match all constructors, fields, or methods. +

+A class member setting doesn't have a closure. + +

Alternative: imported Ant task

+ +Instead of using the Gradle task, you could also integrate the Ant task in +your Gradle build file: +

+

+ant.project.basedir = '../..'
+
+ant.taskdef(resource: 'proguard/ant/task.properties',
+            classpath: '/usr/local/java/proguard/lib/proguard.jar')
+
+

+ +Gradle automatically converts the elements and attributes to Groovy methods, +so converting the configuration is essentially mechanical. The one-on-one +mapping can be useful, but the resulting configuration is more verbose. For +instance: +

+task proguard << {
+  ant.proguard(printmapping: 'proguard.map',
+               overloadaggressively: 'on',
+               repackageclasses: '',
+               renamesourcefileattribute: 'SourceFile') {
+
+    injar(file: 'application.jar')
+    injar(file: 'gui.jar', filter: '!META-INF/**')
+
+    .....
+  }
+}
+
+

+ +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/gui.html b/docs/manual/gui.html new file mode 100644 index 000000000..6677aaf26 --- /dev/null +++ b/docs/manual/gui.html @@ -0,0 +1,481 @@ + + + + + + +ProGuard GUI + + + + +

Graphical User Interface

+ +You can find the ProGuard GUI jar in the lib directory of the +ProGuard distribution. To run the ProGuard graphical user interface, just type: +

+java -jar proguardgui.jar [-nosplash] [configuration_file] +

+Alternatively, the bin directory contains some short Linux and +Windows scripts containing this command. The GUI will pop up in a window. With +the -nosplash option, you can switch off the short opening +animation. If you have specified a ProGuard configuration file, it will be +loaded. The GUI works like a wizard. You can edit the configuration and +execute ProGuard through a few tabs: +

+ + + + + + + + + + + + + + + + +
ProGuardOptionally load an existing configuration file.
Input/OutputSpecify the program jars and library jars.
ShrinkingSpecify the shrinking options.
ObfuscationSpecify the obfuscation options.
OptimizationSpecify the optimization options.
InformationSpecify some options to get information.
ProcessView and save the resulting configuration, and run ProGuard.
+

+ +In addition, there is a tab to execute ReTrace interactively: +

+ + + + +
ReTraceSet up and run ReTrace, to de-obfuscate stack traces.
+

+ +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 Previous and +Next 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 Introduction of this manual. +

+ +

The ProGuard Tab

+ +The ProGuard tab presents a welcome message and one important button at +the bottom: +

+ + + + +
Load configuration...opens a file chooser to load an existing ProGuard configuration + file.
+

+ +If you don't want to load an existing configuration, you can just continue +creating a new configuration from scratch. +

+ +

The Input/Output Tab

+ +The Input/Output tab contains two lists, respectively to specify the +program jars (or wars, ears, zips, or directories), and the library jars (or +wars, ears, zips, or directories). + +
    +
  • The list of program jars contains input entries and output entries. Input + entries contain the class files and resource files to be processed. Output + entries specify the destinations to which the processed results will be + written. They are preceded by arrows, to distinguish them from input + entries. The results of each consecutive list of input entries will be + written to the subsequent consecutive list of output entries.
  • + +
  • The library jars are not copied to the output jars; they contain class + files that are used by class files in the program jars and that are + necessary for correct processing. This list typically at least contains the + targeted Java runtime jar.
  • +
+

+ +Each of these lists can be edited by means of a couple of buttons on the +right-hand side: +

+ + + + + + + + + + + + + + + + + + + + +
Add input... opens a file chooser to add an + input entry to the list of program jars.
Add output... opens a file chooser to add an + output entry to the list of program jars.
Add...opens a file chooser to add an entry to the list of library + jars.
Edit...opens a file chooser to edit the selected entry in the list.
Filter...opens a text entry field to add or edit the filters of the selected + entries in the list.
Removeremoves the selected entries from the list.
Move upmoves the selected entries one position up the list.
Move downmoves the selected entries one position down the list.
Move to librariesmoves the selected entries in the list of program jars to the list of + library jars.
Move to programmoves the selected entries in the list of library jars to the list of + program jars.
+

+ +Filters allow to filter files based on their names. One can specify filters +for class file names and resource file names, for jar file names, for war file +names, for ear file names, and for zip file names. Multiple entries in the +program list only make sense when combined with filters; each output file is +written to the first entry with a matching filter. +

+ +Input entries that are currently not readable are colored red. +

+ +The order of the entries in each list may matter, as the first occurrence of +any duplicate entries gets precedence, just as in conventional class paths. +

+ +Corresponding configuration options: +

+

+ +

The Shrinking Tab

+ +The Shrinking tab presents a number of options that affect the +shrinking step. The basic options are followed by a few lists of classes and +class members (fields and methods) that must be protected from shrinking (and +implicitly from obfuscation as well). +

+ +The fixed lists contain predefined entries that are typically useful for many +applications. Each of these entries can be toggled by means of a check box. +The text field following each entry allows to constrain the applicable classes +by means of a comma-separated list of wildcarded, fully-qualified class +names. The default is "*", which means that all input classes of the +corresponding type are considered. +

+ +For example, checking the Applications entry and filling in +"myapplications.**" after it would mean: keep all classes that have main +methods in the "myapplications" package and all of its subpackages. +

+ +The variable list at the bottom allows to define additional entries +yourself. The list can be edited by means of a couple of buttons on the +right-hand side: +

+ + + + + + + + + + + + +
Add...opens a window to add a new entry to the list.
Edit...opens a window to edit the selected entry in the list.
Removeremoves the selected entries from the list.
Move upmoves the selected entries one position up the list.
Move downmoves the selected entries one position down the list.
+

+ +The interface windows allow to specify classes, fields, and methods. They +contain text fields and check boxes to constrain these items. They have +Ok and Cancel buttons to apply or to cancel the operation. +

+ +For example, your application may be creating some classes dynamically using +Class.forName. You should then specify them here, so they are kept +by their original names. Press the Add... button to open the class +window. Fill out the fully-qualified class name in the Code text field, +and press the Ok button. Repeat this for all required classes. Wildcards +can be helpful to specify a large number of related classes in one go. If you +want to specify all implementations of a certain interface, fill out the +fully qualified interface name in the Extends/implements class instead. +

+ +For more advanced settings, it is advisable to become familiar with ProGuard's +configuration options through the Usage section and +the Examples section. We'll suffice with a brief +overview of the three dialogs provided by the GUI. +

+ +The keep class 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. The Advanced options / Basic +options button at the bottom of the dialog allows to toggle showing the +advanced options. + +

    +
  • The Comments text field allows to add optional comments to this + entry. The comments will identify the entry in the list and they will + appear as comments in the configuration file.
  • + +
  • The Keep selection allows to specify whether you want to protect + the specified classes and their specified class members, or just the + specified class members from the specified classes, or the specified + classes and the specified class members, if the class members are present. + Note that class members will only be protected if they are explicitly + specified, even if only by means of a wildcard.
  • + +
  • The Allow 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.
  • + +
  • The Access selections allows to specify constraints on the class or + classes, based on their access modifiers.
  • + +
  • The Annotation 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 keep + annotations.
  • + +
  • The Class text field takes the fully-qualified name of the class or + classes. The class name can contain wildcards.
  • + +
  • The Annotation 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 keep annotations.
  • + +
  • The Extends/implements class text field takes the fully-qualified + name of the class or interface that the above classes must extend.
  • + +
  • The Class members list allows to specify a list of fields and + methods to keep. It can be edited by means of a list of buttons on the + right-hand side.
  • +
+

+ +The keep field dialog appears when adding or editing fields within the +above dialog. It has text fields and selections for specifying and +constraining fields to keep. Again, the Advanced options / Basic +options button at the bottom of the dialog allows to toggle showing the +advanced options. + +

    +
  • The Access selections allows to specify constraints on the field or + fields, based on their access modifiers.
  • + +
  • The Annotation 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 keep + annotations.
  • + +
  • The Return type text field takes the fully-qualified type of the + field or fields. The type can contain wildcards.
  • + +
  • The Name text field takes the name of the field or fields. The field + name can contain wildcards.
  • +
+

+ +Similarly, the keep method 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 Advanced +options / Basic options button at the bottom of the dialog allows +to toggle showing the advanced options. + +

    +
  • The Access selections allows to specify constraints on the method or + methods, based on their access modifiers.
  • + +
  • The Annotation 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 keep + annotations.
  • + +
  • The Return type text field takes the fully-qualified type of the method or methods. The type can contain wildcards.
  • + +
  • The Name text field takes the name of the method or methods. The + method name can contain wildcards.
  • + +
  • The Arguments text field takes the comma-separated list of + fully-qualified method arguments. Each of these arguments can contain + wildcards.
  • +
+

+ +Corresponding configuration options: +

+

+ +

The Obfuscation Tab

+ +The Obfuscation tab presents a number of options that affect the +obfuscation step. The basic options are followed by a few lists of classes and +class members (fields and methods) that must be protected from obfuscation +(but not necessarily from shrinking). +

+ +The lists are manipulated in the same way as in the Shrinking Tab. +

+ +Corresponding configuration options: +

+

+ +

The Optimization Tab

+ +The Optimization tab presents a number of options that affect the +optimization step. The basic options are followed by a few lists of class +method calls that can be removed if ProGuard can determine that their results +are not being used. +

+ +The lists are manipulated in much the same way as in the Shrinking Tab. +

+ +Corresponding configuration options: +

+

+ +

The Information Tab

+ +The Information 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. +

+ +Corresponding configuration options: +

+

+ +

The Process Tab

+ +The Process tab has an output console for displaying the configuration +and the messages while processing. There are three important buttons at the +bottom: +

+ + + + + + + + +
View configurationdisplays the current ProGuard configuration in the console.
Save configuration...opens a file chooser to save the current ProGuard + configuration.
Process!executes ProGuard with the current configuration.
+

+ +

The ReTrace Tab

+ +The ReTrace tab has a panel with a few settings, an input text area for +the obfuscated stack trace, and an output console to view the de-obfuscated +stack trace: + +
    +
  • The Verbose check box in the settings panel allows to toggle between + normal mode and verbose mode.
  • + +
  • The Mapping file 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 Browse... button that + opens a file chooser.
  • + +
  • The Obfuscated stack trace 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.
  • +
+ +There are two buttons at the bottom: +

+ + + + + + +
Load stack trace...opens a file chooser to load an obfuscated stack trace.
ReTrace!executes ReTrace with the current settings.
+ +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/index.html b/docs/manual/index.html new file mode 100644 index 000000000..643a216ed --- /dev/null +++ b/docs/manual/index.html @@ -0,0 +1,52 @@ + + + + + + +ProGuard Manual + + + + +

ProGuard

+ +
    +
  1. Introduction
  2. +
  3. Usage
  4. +
  5. Limitations
  6. +
  7. Examples
  8. +
  9. Troubleshooting
  10. +
  11. Reference Card
  12. +
  13. Graphical User Interface
  14. +
  15. Ant Task
  16. +
  17. Gradle Task
  18. +
  19. JME Wireless Toolkit Integration
  20. +
+ +

ReTrace

+ +
    +
  1. Introduction
  2. +
  3. Usage
  4. +
  5. Examples
  6. +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/introduction.html b/docs/manual/introduction.html new file mode 100644 index 000000000..3f181144c --- /dev/null +++ b/docs/manual/introduction.html @@ -0,0 +1,172 @@ + + + + + + +ProGuard Introduction + + + + +

Introduction

+ +ProGuard 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 and for Java 6 and higher. +

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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Input jars
Shrunk code
Optim. codeOutput jars
- shrink →- optimize →- obfuscate →Obfusc. code- preverify →
Library jars------------------------------- (unchanged) -------------------------------→Library jars
+

+ +ProGuard first reads the input jars (or wars, ears, zips, or +directories). It then subsequently shrinks, optimizes, obfuscates, and +preverifies them. You can optionally let ProGuard perform multiple +optimization passes. ProGuard writes the processed results to one or +more output jars (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. +

+ProGuard requires the library jars (or wars, ears, zips, or +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. + +

Entry points

+ +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 code. These entry points are typically classes with main methods, applets, +midlets, activities, etc. +
    +
  • In the shrinking step, ProGuard starts from these seeds and + recursively determines which classes and class members are used. All other + classes and class members are discarded.
  • + +
  • In the optimization step, ProGuard further optimizes the code. + Among other optimizations, classes and methods that are not entry points + can be made private, static, or final, unused parameters can be removed, + and some methods may be inlined.
  • + +
  • In the obfuscation step, 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.
  • + +
  • The preverification step is the only step that doesn't have to know + the entry points.
  • +
+

+The Usage section of this manual describes the +necessary -keep options and +the Examples section provides plenty of examples. + +

Reflection

+ +Reflection and introspection present 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, Class.forName() constructs may +refer to any class at run-time. It is generally impossible to compute 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 -keep options. +

+However, ProGuard will already detect and handle the following cases for you: + +

    +
  • Class.forName("SomeClass")
  • +
  • SomeClass.class
  • +
  • SomeClass.class.getField("someField")
  • +
  • SomeClass.class.getDeclaredField("someField")
  • +
  • SomeClass.class.getMethod("someMethod", new Class[] {})
  • +
  • SomeClass.class.getMethod("someMethod", new Class[] { A.class })
  • +
  • SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })
  • +
  • SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})
  • +
  • SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })
  • +
  • SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })
  • +
  • AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")
  • +
  • AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")
  • +
  • AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")
  • +
+ +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 updated in the obfuscation phase. +

+Furthermore, ProGuard will offer some suggestions if keeping some classes or +class members appears necessary. For example, ProGuard will note constructs +like "(SomeClass)Class.forName(variable).newInstance()". These +might be an indication that the class or interface SomeClass +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 reflection +may require trial and error, especially without the necessary information +about the internals of the code. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/limitations.html b/docs/manual/limitations.html new file mode 100644 index 000000000..6b6941097 --- /dev/null +++ b/docs/manual/limitations.html @@ -0,0 +1,69 @@ + + + + + + +ProGuard Limitations + + + + +

Limitations

+ +When using ProGuard, you should be aware of a few technical issues, all of +which are easily avoided or resolved: +

+

    + +
  • For best results, ProGuard's optimization algorithms assume that the + processed code never intentionally throws NullPointerExceptions or + ArrayIndexOutOfBoundsExceptions, or even OutOfMemoryErrors or + StackOverflowErrors, in order to achieve something useful. For instance, + it may remove a method call myObject.myMethod() if that call + wouldn't have any effect. It ignores the possibility that + myObject might be null, causing a NullPointerException. In + some way this is a good thing: optimized code may throw fewer exceptions. + Should this entire assumption be false, you'll have to switch off + optimization using the -dontoptimize option.
  • + +
  • ProGuard's optimization algorithms currently also assume that the + processed code never creates busy-waiting loops without at least + testing on a volatile field. Again, it may remove such loops. Should this + assumption be false, you'll have to switch off optimization using + the -dontoptimize option.
  • + +
  • If an input jar and a library jar contain classes in the same + package, the obfuscated output jar may contain class names that + overlap with class names in the library jar. This is most likely if the + library jar has been obfuscated before, as it will then probably contain + classes named 'a', 'b', etc. Packages should therefore never be split + across input jars and library jars.
  • + +
  • When obfuscating, ProGuard writes out class files named + "a.class", "b.class", etc. If a package contains + a large number of classes, ProGuard may also write out + "aux.class". Inconveniently, Windows refuses to create + files with this reserved name (among a few other names). It's generally + better to write the output to a jar, in order to avoid such problems.
  • + +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/optimizations.html b/docs/manual/optimizations.html new file mode 100644 index 000000000..e4c96b6f7 --- /dev/null +++ b/docs/manual/optimizations.html @@ -0,0 +1,198 @@ + + + + + + +Optimizations + + + + +

Optimizations

+ +The optimization step of ProGuard can be switched off with the +-dontoptimize option. For +more fine-grained control over individual optimizations, experts can use the +-optimizations option, +with a filter based on the optimization names listed below. The filter works +like any filter in ProGuard. +

+ +The following wildcards are supported: + + + + + + +
?matches any single character in an optimization name.
*matches any part of an optimization name.
+ +An optimization that is preceded by an exclamation mark '!' is +excluded from further attempts to match with subsequent +optimization names in the filter. Make sure to specify filters correctly, +since they are not checked for potential typos. +

+ +For example, +"code/simplification/variable,code/simplification/arithmetic" +only performs the two specified peephole optimizations. +

+ +For example, "!method/propagation/*" performs all optimizations, +except the ones that propagate values between methods. +

+ +For example, +"!code/simplification/advanced,code/simplification/*" only +performs all peephole optimizations. +

+Some optimizations necessarily imply other optimizations. These are then +indicated. Note that the list is likely to change over time, as optimizations +are added and reorganized. +

+ +

+
class/marking/final
+
Marks classes as final, whenever possible.
+ +
class/merging/vertical
+
Merges classes vertically in the class hierarchy, whenever possible.
+ +
class/merging/horizontal
+
Merges classes horizontally in the class hierarchy, whenever possible.
+ +
(⇒ code/removal/advanced)
+ field/removal/writeonly
+
Removes write-only fields.
+ +
field/marking/private
+
Marks fields as private, whenever possible.
+ +
(⇒ code/simplification/advanced)
+ field/propagation/value
+
Propagates the values of fields across methods.
+ +
method/marking/private
+
Marks methods as private, whenever possible (devirtualization).
+ +
(⇒ code/removal/advanced)
+ method/marking/static
+
Marks methods as static, whenever possible (devirtualization).
+ +
method/marking/final
+
Marks methods as final, whenever possible.
+ +
(⇒ code/removal/advanced)
+ method/removal/parameter
+
Removes unused method parameters.
+ +
(⇒ code/simplification/advanced)
+ method/propagation/parameter
+
Propagates the values of method parameters from method invocations to + the invoked methods.
+ +
(⇒ code/simplification/advanced)
+ method/propagation/returnvalue
+
Propagates the values of method return values from methods to their + invocations.
+ +
method/inlining/short
+
Inlines short methods.
+ +
method/inlining/unique
+
Inlines methods that are only called once.
+ +
method/inlining/tailrecursion
+
Simplifies tail recursion calls, whenever possible.
+ +
code/merging
+
Merges identical blocks of code by modifying branch targets.
+ +
code/simplification/variable
+
Performs peephole optimizations for variable loading and storing.
+ +
code/simplification/arithmetic
+
Performs peephole optimizations for arithmetic instructions.
+ +
code/simplification/cast
+
Performs peephole optimizations for casting operations.
+ +
code/simplification/field
+
Performs peephole optimizations for field loading and storing.
+ +
(⇒ code/removal/simple)
+ code/simplification/branch
+
Performs peephole optimizations for branch instructions.
+ +
code/simplification/string
+
Performs peephole optimizations for constant strings.
+ +
(best used with code/removal/advanced)
+ code/simplification/advanced
+
Simplifies code based on control flow analysis and data flow + analysis.
+ +
(⇒ code/removal/exception)
+ code/removal/advanced
+
Removes dead code based on control flow analysis and data flow + analysis.
+ +
(⇒ code/removal/exception)
+ code/removal/simple
+
Removes dead code based on a simple control flow analysis.
+ +
code/removal/variable
+
Removes unused variables from the local variable frame.
+ +
code/removal/exception
+
Removes exceptions with empty try blocks.
+ +
code/allocation/variable
+
Optimizes variable allocation on the local variable frame.
+
+

+ +ProGuard also provides some unofficial settings to control optimizations, that +may disappear in future versions. These are Java system properties, which +can be set as JVM arguments (with -D.....): +

+
maximum.inlined.code.length (default = 8 bytes)
+
Specifies the maximum code length (expressed in bytes) of short methods + that are eligible to be inlined. Inlining methods that are too long may + unnecessarily inflate the code size.
+ +
maximum.resulting.code.length (default = 8000 bytes + for JSE, 2000 bytes for JME)
+
Specifies the maximum resulting code length (expressed in bytes) allowed + when inlining methods. Many Java virtual machines do not apply just-in-time + compilation to methods that are too long, so it's important not to let them + grow too large.
+ +
optimize.conservatively (default = unset)
+
Allows input code with ordinary instructions intentionally throwing + NullPointerException, + ArrayIndexOutOfBoundsException, or + ClassCastException, without any other useful purposes. By + default, ProGuard may just discard such seemingly useless instructions, + resulting in better optimization of most common code.
+
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/refcard.html b/docs/manual/refcard.html new file mode 100644 index 000000000..d9301bdb8 --- /dev/null +++ b/docs/manual/refcard.html @@ -0,0 +1,486 @@ + + + + + + +ProGuard Reference Card + + + + +

ProGuard Reference Card

+ +

Usage

+ +java -jar proguard.jar options ... +

+  Typically: +

+java -jar proguard.jar @myconfig.pro +

+ +

Options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@filenameShort for '-include filename'.
-include + filenameRead configuration options from the given file.
-basedirectory + directorynameSpecifies the base directory for subsequent relative file names.
-injars + class_pathSpecifies the program jars (or wars, ears, zips, or directories).
-outjars + class_pathSpecifies the names of the output jars (or wars, ears, zips, or + directories).
-libraryjars + class_pathSpecifies the library jars (or wars, ears, zips, or directories).
-skipnonpubliclibraryclassesIgnore non-public library classes.
-dontskipnonpubliclibraryclassesDon't ignore non-public library classes (the default).
-dontskipnonpubliclibraryclassmembersDon't ignore package visible library class members.
-keepdirectories + [directory_filter]Keep the specified directories in the output jars (or wars, ears, zips, or + directories).
-target + versionSet the given version number in the processed classes.
-forceprocessingProcess the input, even if the output seems up to date.
-keep + [,modifier,...] + class_specificationPreserve the specified classes and class members.
-keepclassmembers + [,modifier,...] + class_specificationPreserve the specified class members, if their classes are preserved as + well.
-keepclasseswithmembers + [,modifier,...] + class_specificationPreserve the specified classes and class members, if all of the + specified class members are present.
-keepnames + class_specificationPreserve the names of the specified classes and class members (if + they aren't removed in the shrinking step).
-keepclassmembernames + class_specificationPreserve the names of the specified class members (if they aren't removed + in the shrinking step).
-keepclasseswithmembernames + class_specificationPreserve the names of the specified classes and class members, if + all of the specified class members are present (after the shrinking + step).
-printseeds + [filename]List classes and class members matched by the various -keep + options, to the standard output or to the given file.
-dontshrinkDon't shrink the input class files.
-printusage + [filename]List dead code of the input class files, to the standard output or to the + given file.
-whyareyoukeeping + class_specificationPrint details on why the given classes and class members are being kept in + the shrinking step.
-dontoptimizeDon't optimize the input class files.
-optimizations + optimization_filterThe optimizations to be enabled and disabled.
-optimizationpasses + nThe number of optimization passes to be performed.
-assumenosideeffects + class_specificationAssume that the specified methods don't have any side effects, while + optimizing.
-allowaccessmodificationAllow the access modifiers of classes and class members to be modified, + while optimizing.
-mergeinterfacesaggressivelyAllow any interfaces to be merged, while optimizing.
-dontobfuscateDon't obfuscate the input class files.
-printmapping + [filename]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.
-applymapping + filenameReuse the given mapping, for incremental obfuscation.
-obfuscationdictionary + filenameUse the words in the given text file as obfuscated field names and method names.
-classobfuscationdictionary + filenameUse the words in the given text file as obfuscated class names.
-packageobfuscationdictionary + filenameUse the words in the given text file as obfuscated package names.
-overloadaggressivelyApply aggressive overloading while obfuscating.
-useuniqueclassmembernamesEnsure uniform obfuscated class member names for subsequent incremental + obfuscation.
-dontusemixedcaseclassnamesDon't generate mixed-case class names while obfuscating.
-keeppackagenames + [package_filter]Keep the specified package names from being obfuscated.
-flattenpackagehierarchy + [package_name]Repackage all packages that are renamed into the single given parent + package.
-repackageclasses + [package_name]Repackage all class files that are renamed into the single given + package.
-keepattributes + [attribute_filter]Preserve the given optional attributes; typically + Exceptions, InnerClasses, + Signature, Deprecated, + SourceFile, SourceDir, + LineNumberTable, + LocalVariableTable, LocalVariableTypeTable, + Synthetic, EnclosingMethod, and + *Annotation*.
-keepparameternamesKeep the parameter names and types of methods that are kept.
-renamesourcefileattribute + [string]Put the given constant string in the SourceFile + attributes.
-adaptclassstrings + [class_filter]Adapt string constants in the specified classes, based on the obfuscated + names of any corresponding classes.
-adaptresourcefilenames + [file_filter]Rename the specified resource files, based on the obfuscated names of the + corresponding class files.
-adaptresourcefilecontents + [file_filter]Update the contents of the specified resource files, based on the + obfuscated names of the processed classes.
-dontpreverifyDon't preverify the processed class files.
-microeditionTarget the processed class files at Java Micro Edition.
-verboseWrite out some more information during processing.
-dontnote + [class_filter]Don't print notes about potential mistakes or omissions in the + configuration.
-dontwarn + [class_filter]Don't warn about unresolved references at all.
-ignorewarningsPrint warnings about unresolved references, but continue processing + anyhow.
-printconfiguration + [filename]Write out the entire configuration in traditional ProGuard style, to the + standard output or to the given file.
-dump + [filename]Write out the internal structure of the processed class files, to the + standard output or to the given file.
+

+Notes: +

    + +
  • class_path is a list of jars, wars, ears, zips, and directories, + with optional filters, separated by path separators.
  • +
  • filename can contain Java system properties delimited by + '<' and '>'.
  • +
  • If filename contains special characters, the entire name + should be quoted with single or double quotes.
  • +
+

+ +

Overview of Keep Options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeepFrom being removed or renamedFrom being renamed
Classes and class members-keep-keepnames
Class members only-keepclassmembers-keepclassmembernames
Classes and class members, if class members present-keepclasseswithmembers-keepclasseswithmembernames
+

+ +

Keep Option Modifiers

+ + + + + + + + + + + + + + + + + + +
allowshrinkingThe entry points specified in the keep tag may be shrunk.
allowoptimizationThe entry points specified in the keep tag may be optimized.
allowobfuscationThe entry points specified in the keep tag may be obfuscated.
+

+ +

Class Specifications

+ +
+[@annotationtype] [[!]public|final|abstract ...] [!]interface|class classname
+    [extends|implements [@annotationtype] classname]
+[{
+    [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |
+                                                                      (fieldtype fieldname);
+    [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |
+                                                                                           <init>(argumenttype,...) |
+                                                                                           classname(argumenttype,...) |
+                                                                                           (returntype methodname(argumenttype,...));
+    [@annotationtype] [[!]public|private|protected|static ... ] *;
+    ...
+}]
+
+

+Notes: +

    +
  • Class names must always be fully qualified, i.e. including their package + names.
  • +
  • Types in classname, annotationtype, returntype, and + argumenttype can contain wildcards: '?' for a + single character, '*' for any number of characters + (but not the package separator), '**' for any number + of (any) characters, '%' for any primitive type, + '***' for any type, and '...' for any number of arguments.
  • +
  • fieldname and methodname can contain wildcards as well: + '?' for a single character and '*' + for any number of characters.
  • +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/retrace/examples.html b/docs/manual/retrace/examples.html new file mode 100644 index 000000000..2137e3a8c --- /dev/null +++ b/docs/manual/retrace/examples.html @@ -0,0 +1,345 @@ + + + + + + +ReTrace Examples + + + + +

Examples

+ +Some typical example uses: +
    +
  1. Restoring a stack trace with line numbers
  2. +
  3. Restoring a stack trace with line numbers + (verbose)
  4. +
  5. Restoring a stack trace without line numbers
  6. +
+ +

Restoring a stack trace with line numbers

+ +Assume for instance ProGuard itself has been obfuscated using the following +extra options: +
+-printmapping proguard.map
+
+-renamesourcefileattribute ProGuard
+-keepattributes SourceFile,LineNumberTable
+
+

+ +Now assume the processed application throws an exception, and we have saved the +stack trace in proguard.trace, shown below. Of course, in real +life ProGuard rarely throws exceptions, so this is a purposely generated +exception. :) + +

+Exception in thread "main" java.lang.Error: Random exception
+        at pro.bY.a(ProGuard:576)
+        at pro.bO.a(ProGuard:431)
+        at pro.bj.a(ProGuard:145)
+        at pro.bY.a(ProGuard:522)
+        at pro.bj.a(ProGuard:129)
+        at pro.bN.a(ProGuard:125)
+        at pro.bY.a(ProGuard:251)
+        at pro.bY.a(ProGuard:229)
+        at pro.l.a(ProGuard:55)
+        at pro.bo.b(ProGuard:405)
+        at pro.ci.a(ProGuard:51)
+        at pro.bo.a(ProGuard:356)
+        at pro.be.a(ProGuard:109)
+        at pro.bo.a(ProGuard:356)
+        at pro.be.a(ProGuard:186)
+        at pro.bg.a(ProGuard:369)
+        at pro.bY.a(ProGuard:286)
+        at pro.bh.a(ProGuard:55)
+        at pro.bg.b(ProGuard:408)
+        at pro.bY.a(ProGuard:190)
+        at pro.bg.a(ProGuard:369)
+        at pro.M.a(ProGuard:110)
+        at pro.bY.a(ProGuard:449)
+        at pro.M.a(ProGuard:99)
+        at pro.bo.a(ProGuard:372)
+        at pro.bY.a(ProGuard:649)
+        at pro.bY.a(ProGuard:112)
+        at pro.P.a(ProGuard:66)
+        at pro.p.a(ProGuard:83)
+        at pro.bU.a(ProGuard:69)
+        at pro.bo.a(ProGuard:356)
+        at pro.J.a(ProGuard:149)
+        at pro.I.a(ProGuard:49)
+        at pro.J.a(ProGuard:105)
+        at pro.cf.c(ProGuard:370)
+        at pro.cf.a(ProGuard:317)
+        at pro.bc.a(ProGuard:55)
+        at proguard.ProGuard.a(ProGuard:363)
+        at proguard.ProGuard.c(ProGuard:187)
+        at proguard.ProGuard.b(ProGuard:385)
+        at proguard.ProGuard.main(ProGuard:429)
+
+

+ +We can then use the following command to recover the stack trace: +

+java -jar retrace.jar proguard.map proguard.trace
+
+

+ +The output will look as follows: +

+Exception in thread "main" java.lang.Error: Random exception
+        at proguard.shrink.UsageMarker.visitInstruction(ProGuard:576)
+        at proguard.classfile.instruction.GenericInstruction.accept(ProGuard:431)
+        at proguard.classfile.CodeAttrInfo.instructionsAccept(ProGuard:145)
+        at proguard.shrink.UsageMarker.visitCodeAttrInfo(ProGuard:522)
+        at proguard.classfile.CodeAttrInfo.accept(ProGuard:129)
+        at proguard.classfile.ProgramMemberInfo.attributesAccept(ProGuard:125)
+        at proguard.shrink.UsageMarker.visitMemberInfo(ProGuard:251)
+        at proguard.shrink.UsageMarker.visitProgramMethodInfo(ProGuard:229)
+        at proguard.classfile.ProgramMethodInfo.accept(ProGuard:55)
+        at proguard.classfile.ProgramClassFile.methodAccept(ProGuard:405)
+        at proguard.classfile.visitor.NamedMethodVisitor.visitProgramClassFile(ProGuard:51)
+        at proguard.classfile.ProgramClassFile.accept(ProGuard:356)
+        at proguard.classfile.visitor.ClassFileUpDownTraveler.visitProgramClassFile(ProGuard:109)
+        at proguard.classfile.ProgramClassFile.accept(ProGuard:356)
+        at proguard.classfile.visitor.ClassFileUpDownTraveler.visitLibraryClassFile(ProGuard:186)
+        at proguard.classfile.LibraryClassFile.accept(ProGuard:369)
+        at proguard.shrink.UsageMarker.visitLibraryMethodInfo(ProGuard:286)
+        at proguard.classfile.LibraryMethodInfo.accept(ProGuard:55)
+        at proguard.classfile.LibraryClassFile.methodsAccept(ProGuard:408)
+        at proguard.shrink.UsageMarker.visitLibraryClassFile(ProGuard:190)
+        at proguard.classfile.LibraryClassFile.accept(ProGuard:369)
+        at proguard.classfile.ClassCpInfo.referencedClassAccept(ProGuard:110)
+        at proguard.shrink.UsageMarker.visitClassCpInfo(ProGuard:449)
+        at proguard.classfile.ClassCpInfo.accept(ProGuard:99)
+        at proguard.classfile.ProgramClassFile.constantPoolEntryAccept(ProGuard:372)
+        at proguard.shrink.UsageMarker.markCpEntry(ProGuard:649)
+        at proguard.shrink.UsageMarker.visitProgramClassFile(ProGuard:112)
+        at proguard.classfile.visitor.VariableClassFileVisitor.visitProgramClassFile(ProGuard:66)
+        at proguard.classfile.visitor.MultiClassFileVisitor.visitProgramClassFile(ProGuard:83)
+        at proguard.classfile.visitor.FilteredClassFileVisitor.visitProgramClassFile(ProGuard:69)
+        at proguard.classfile.ProgramClassFile.accept(ProGuard:356)
+        at proguard.classfile.ClassPool.classFileAccept(ProGuard:149)
+        at proguard.classfile.visitor.NamedClassFileVisitor.visitClassPool(ProGuard:49)
+        at proguard.classfile.ClassPool.accept(ProGuard:105)
+        at proguard.KeepCommand.executeShrinkingPhase(ProGuard:370)
+        at proguard.KeepCommand.execute(ProGuard:317)
+        at proguard.CompoundCommand.execute(ProGuard:55)
+        at proguard.ProGuard.executeCommands(ProGuard:363)
+        at proguard.ProGuard.shrink(ProGuard:187)
+        at proguard.ProGuard.execute(ProGuard:385)
+        at proguard.ProGuard.main(ProGuard:429)
+
+ +

Restoring a stack trace with line numbers (verbose)

+ +In the previous example, we could also use the verbose flag: +
+java -jar retrace.jar -verbose proguard.map proguard.trace
+
+

+ +The output will then look as follows: +

+Exception in thread "main" java.lang.Error: Random exception
+        at proguard.shrink.UsageMarker.void visitInstruction(proguard.classfile.ClassFile,proguard.classfile.instruction.Instruction)(ProGuard:576)
+        at proguard.classfile.instruction.GenericInstruction.void accept(proguard.classfile.ClassFile,proguard.classfile.instruction.InstructionVisitor)(ProGuard:431)
+        at proguard.classfile.CodeAttrInfo.void instructionsAccept(proguard.classfile.ClassFile,proguard.classfile.instruction.InstructionVisitor)(ProGuard:145)
+        at proguard.shrink.UsageMarker.void visitCodeAttrInfo(proguard.classfile.ClassFile,proguard.classfile.CodeAttrInfo)(ProGuard:522)
+        at proguard.classfile.CodeAttrInfo.void accept(proguard.classfile.ClassFile,proguard.classfile.visitor.AttrInfoVisitor)(ProGuard:129)
+        at proguard.classfile.ProgramMemberInfo.void attributesAccept(proguard.classfile.ProgramClassFile,proguard.classfile.visitor.AttrInfoVisitor)(ProGuard:125)
+        at proguard.shrink.UsageMarker.void visitMemberInfo(proguard.classfile.ProgramClassFile,proguard.classfile.ProgramMemberInfo)(ProGuard:251)
+        at proguard.shrink.UsageMarker.void visitProgramMethodInfo(proguard.classfile.ProgramClassFile,proguard.classfile.ProgramMethodInfo)(ProGuard:229)
+        at proguard.classfile.ProgramMethodInfo.void accept(proguard.classfile.ProgramClassFile,proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:55)
+        at proguard.classfile.ProgramClassFile.void methodAccept(proguard.classfile.visitor.MemberInfoVisitor,java.lang.String,java.lang.String)(ProGuard:405)
+        at proguard.classfile.visitor.NamedMethodVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:51)
+        at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356)
+        at proguard.classfile.visitor.ClassFileUpDownTraveler.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:109)
+        at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356)
+        at proguard.classfile.visitor.ClassFileUpDownTraveler.void visitLibraryClassFile(proguard.classfile.LibraryClassFile)(ProGuard:186)
+        at proguard.classfile.LibraryClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:369)
+        at proguard.shrink.UsageMarker.void visitLibraryMethodInfo(proguard.classfile.LibraryClassFile,proguard.classfile.LibraryMethodInfo)(ProGuard:286)
+        at proguard.classfile.LibraryMethodInfo.void accept(proguard.classfile.LibraryClassFile,proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:55)
+        at proguard.classfile.LibraryClassFile.void methodsAccept(proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:408)
+        at proguard.shrink.UsageMarker.void visitLibraryClassFile(proguard.classfile.LibraryClassFile)(ProGuard:190)
+        at proguard.classfile.LibraryClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:369)
+        at proguard.classfile.ClassCpInfo.void referencedClassAccept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:110)
+        at proguard.shrink.UsageMarker.void visitClassCpInfo(proguard.classfile.ClassFile,proguard.classfile.ClassCpInfo)(ProGuard:449)
+        at proguard.classfile.ClassCpInfo.void accept(proguard.classfile.ClassFile,proguard.classfile.visitor.CpInfoVisitor)(ProGuard:99)
+        at proguard.classfile.ProgramClassFile.void constantPoolEntryAccept(proguard.classfile.visitor.CpInfoVisitor,int)(ProGuard:372)
+        at proguard.shrink.UsageMarker.void markCpEntry(proguard.classfile.ClassFile,int)(ProGuard:649)
+        at proguard.shrink.UsageMarker.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:112)
+        at proguard.classfile.visitor.VariableClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:66)
+        at proguard.classfile.visitor.MultiClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:83)
+        at proguard.classfile.visitor.FilteredClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:69)
+        at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356)
+        at proguard.classfile.ClassPool.void classFileAccept(proguard.classfile.visitor.ClassFileVisitor,java.lang.String)(ProGuard:149)
+        at proguard.classfile.visitor.NamedClassFileVisitor.void visitClassPool(proguard.classfile.ClassPool)(ProGuard:49)
+        at proguard.classfile.ClassPool.void accept(proguard.classfile.visitor.ClassPoolVisitor)(ProGuard:105)
+        at proguard.KeepCommand.void executeShrinkingPhase(proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:370)
+        at proguard.KeepCommand.void execute(int,proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:317)
+        at proguard.CompoundCommand.void execute(int,proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:55)
+        at proguard.ProGuard.void executeCommands(int)(ProGuard:363)
+        at proguard.ProGuard.void shrink()(ProGuard:187)
+        at proguard.ProGuard.void execute(java.lang.String[])(ProGuard:385)
+        at proguard.ProGuard.void main(java.lang.String[])(ProGuard:429)
+
+ + +

Restoring a stack trace without line numbers

+ +Assume for instance ProGuard itself has been obfuscated using the following +extra options, this time without preserving the line number tables: +
+-printmapping proguard.map
+
+

+ +A stack trace proguard.trace will then lack line number +information: +

+Exception in thread "main" java.lang.Error: Random exception
+        at pro.bY.a(Unknown Source)
+        at pro.bO.a(Unknown Source)
+        at pro.bj.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bj.a(Unknown Source)
+        at pro.bN.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.l.a(Unknown Source)
+        at pro.bo.b(Unknown Source)
+        at pro.ci.a(Unknown Source)
+        at pro.bo.a(Unknown Source)
+        at pro.be.a(Unknown Source)
+        at pro.bo.a(Unknown Source)
+        at pro.be.a(Unknown Source)
+        at pro.bg.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bh.a(Unknown Source)
+        at pro.bg.b(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bg.a(Unknown Source)
+        at pro.M.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.M.a(Unknown Source)
+        at pro.bo.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.bY.a(Unknown Source)
+        at pro.P.a(Unknown Source)
+        at pro.p.a(Unknown Source)
+        at pro.bU.a(Unknown Source)
+        at pro.bo.a(Unknown Source)
+        at pro.J.a(Unknown Source)
+        at pro.I.a(Unknown Source)
+        at pro.J.a(Unknown Source)
+        at pro.cf.c(Unknown Source)
+        at pro.cf.a(Unknown Source)
+        at pro.bc.a(Unknown Source)
+        at proguard.ProGuard.a(Unknown Source)
+        at proguard.ProGuard.c(Unknown Source)
+        at proguard.ProGuard.b(Unknown Source)
+        at proguard.ProGuard.main(Unknown Source)
+
+

+ +We can still use the same command to recover the stack trace: +

+java -jar retrace.jar proguard.map proguard.trace
+
+

+ +The output will now give a list of alternative original method names for each +ambiguous obfuscated method name: +

+Exception in thread "main" java.lang.Error: Random exception
+        at proguard.shrink.UsageMarker.visitProgramClassFile(Unknown Source)
+                                       visitLibraryClassFile
+                                       visitProgramFieldInfo
+                                       visitProgramMethodInfo
+                                       visitMemberInfo
+                                       visitLibraryFieldInfo
+                                       visitLibraryMethodInfo
+                                       visitIntegerCpInfo
+                                       visitLongCpInfo
+                                       visitFloatCpInfo
+                                       visitDoubleCpInfo
+                                       visitStringCpInfo
+                                       visitUtf8CpInfo
+                                       visitFieldrefCpInfo
+                                       visitInterfaceMethodrefCpInfo
+                                       visitMethodrefCpInfo
+                                       visitClassCpInfo
+                                       visitNameAndTypeCpInfo
+                                       visitUnknownAttrInfo
+                                       visitInnerClassesAttrInfo
+                                       visitConstantValueAttrInfo
+                                       visitExceptionsAttrInfo
+                                       visitCodeAttrInfo
+                                       visitLineNumberTableAttrInfo
+                                       visitLocalVariableTableAttrInfo
+                                       visitSourceFileAttrInfo
+                                       visitDeprecatedAttrInfo
+                                       visitSyntheticAttrInfo
+                                       visitInstruction
+                                       visitCpInstruction
+                                       visitExceptionInfo
+                                       visitInnerClassesInfo
+                                       visitLocalVariableInfo
+                                       markCpEntry
+                                       markAsUnused
+                                       isUsed
+        at proguard.classfile.instruction.GenericInstruction.create(Unknown Source)
+                                                             isWide
+                                                             getLength
+                                                             accept
+        at proguard.classfile.CodeAttrInfo.getAttribute(Unknown Source)
+                                           getAttrInfoLength
+                                           readInfo
+                                           accept
+                                           instructionsAccept
+                                           exceptionsAccept
+        [...]
+        at proguard.KeepCommand.executeShrinkingPhase(Unknown Source)
+                                access$100
+        at proguard.KeepCommand.keepField(Unknown Source)
+                                ensureMultiClassFileVisitorForMembers
+                                execute
+                                executeObfuscationPhase
+                                access$002
+                                access$000
+                                access$102
+                                access$108
+        at proguard.CompoundCommand.addCommand(Unknown Source)
+                                    execute
+        at proguard.ProGuard.readCommands(Unknown Source)
+                             obfuscate
+                             executeCommands
+        at proguard.ProGuard.shrink(Unknown Source)
+        at proguard.ProGuard.check(Unknown Source)
+                             execute
+        at proguard.ProGuard.main(Unknown Source)
+
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/manual/retrace/index.html b/docs/manual/retrace/index.html new file mode 100644 index 000000000..47209df9e --- /dev/null +++ b/docs/manual/retrace/index.html @@ -0,0 +1,37 @@ + + + + + + +ReTrace Manual + + + + +

ReTrace

+ +
    +
  1. Introduction
  2. +
  3. Usage
  4. +
  5. Examples
  6. +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/retrace/introduction.html b/docs/manual/retrace/introduction.html new file mode 100644 index 000000000..381ad1fcd --- /dev/null +++ b/docs/manual/retrace/introduction.html @@ -0,0 +1,79 @@ + + + + + + +ReTrace Introduction + + + + +

Introduction

+ +ReTrace is a companion tool for ProGuard 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. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Original code- ProGuardObfuscated code
Mapping file
Readable stack traceReTrace -Obfuscated stack trace
+

+ReTrace can read an obfuscated stack trace and restore it to what it would +look like without obfuscation. The restoration is based on the mapping file +that ProGuard can write out during obfuscation. The mapping file links the +original class names and class member names to their obfuscated names. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/manual/retrace/usage.html b/docs/manual/retrace/usage.html new file mode 100644 index 000000000..6e7f6a8a2 --- /dev/null +++ b/docs/manual/retrace/usage.html @@ -0,0 +1,128 @@ + + + + + + +ReTrace Usage + + + + +

Usage

+ +You can find the ReTrace jar in the lib directory of the +ProGuard distribution. To run ReTrace, just type: +

+

+java -jar retrace.jar [options...] + mapping_file [stacktrace_file] +

+Alternatively, the bin directory contains some short Linux and +Windows scripts containing this command. These are the arguments: + +
+
mapping_file
+ +
Specifies the name of the mapping file, produced by ProGuard with the + option + "-printmapping mapping_file", + while obfuscating the application that produced the stack trace.
+ +
stacktrace_file
+ +
Optionally specifies the name of the file containing the stack trace. If + no file is specified, a stack trace is read from the standard input. Blank + lines and unrecognized lines are ignored, as far as possible.
+
+ +The following options are supported: +
+
-verbose
+ +
Specifies to print out more informative stack traces that include not only + method names, but also method return types and arguments.
+ +
-regex regular_expression
+ +
Specifies the regular expression that is used to parse the lines in the + stack trace. Specifying a different regular expression allows to + de-obfuscate more general types of input than just stack traces. The + default is suitable for stack traces produced by most JVMs: +
+    (?:.*?\bat\s+%c.%m\s*\(.*?(?::%l)?\)\s*)|(?:(?:.*?[:"]\s+)?%c(?::.*)?)
+    
+ The regular expression is a Java regular expression (cfr. the documentation + of java.util.regex.Pattern), with a few additional wildcards: + + + + + + + + + + + + + + + +
%cmatches a class name (e.g. + "myapplication.MyClass").
%Cmatches a class name with slashes (e.g. + "myapplication/MyClass").
%tmatches a field type or method return type (e.g. + "myapplication.MyClass[]").
%fmatches a field name (e.g. + "myField").
%mmatches a method name (e.g. + "myMethod").
%amatches a list of method arguments (e.g. + "boolean,int").
%lmatches a line number inside a method (e.g. + "123").
+ Elements that match these wildcards are de-obfuscated, when possible. Note + that regular expressions must not contain any capturing groups. Use + non-capturing groups instead: (?:...) +
+
+ +The restored stack trace is printed to the standard output. The completeness +of the restored stack trace depends on the presence of line number tables in +the obfuscated class files: + +
    +
  • If all line numbers have been preserved while obfuscating the application, + ReTrace will be able to restore the stack trace completely.
  • + +
  • If the line numbers have been removed, mapping obfuscated method names + back to their original names has become ambiguous. Retrace will list all + possible original method names for each line in the stack trace. The user + can then try to deduce the actual stack trace manually, based on the logic + of the program.
  • + +
+

+ +Preserving line number tables is explained in detail in this example in the ProGuard User Manual. +

+ +Unobfuscated elements and obfuscated elements for which no mapping is available +will be left unchanged. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/manual/sections.html b/docs/manual/sections.html new file mode 100644 index 000000000..1a6693d3a --- /dev/null +++ b/docs/manual/sections.html @@ -0,0 +1,54 @@ + + + + + + + +Sections + + + + + +

+

+More Android code protection: +

+ +DexGuard + +

+With support of +

+ + +Saikoa + +

+ +SourceForge + +

+ + + diff --git a/docs/manual/style.css b/docs/manual/style.css new file mode 100644 index 000000000..8dae87db0 --- /dev/null +++ b/docs/manual/style.css @@ -0,0 +1,129 @@ +@charset "iso-8859-1"; + +/* Global settings. */ + +body +{ + background: #FFFFFF; +} + +h1 +{ + text-align: center; +} + +h2 +{ + background: #EEEEFF; + padding: 10px; +} + +dt +{ + padding: 6px; +} + +dt div +{ + color: grey; + float: right; +} + +dd +{ + padding: 6px; +} + +pre +{ + padding: 10px; + background: #E0E0E0; +} + +.spacious li +{ + padding: 8px; +} + +.shifted li +{ + margin-left: 50px; +} + +img.float +{ + float: left; +} + +a +{ + text-decoration: none; +} + +a.button +{ + color: #000000; + text-decoration: none; + background: #E0E0E0; + border: 1px outset #FFFFFF; + float: right; +} + +/* Settings for variable width code. */ + +p.code +{ + padding: 10px; + background: #E0E0E0; +} + + +/* Settings for diagrams. */ + +table.diagram +{ + padding: 8px; + border: none; + border-spacing: 2px; +} + +td.transparentblock +{ + text-align: center; + padding: 10px 0px; +} + +td.whiteblock +{ + width: 100px; + text-align: center; + border: 1px solid #C0C0C0; + background: #E0E0E0; + padding: 10px 0px; +} + +td.lightblock +{ + width: 100px; + text-align: center; + border: 1px solid #8888FF; + background: #BBBBFF; + padding: 20px 0px; +} + +td.darkblock +{ + width: 100px; + text-align: center; + background: #8888FF; + padding: 20px 0px; +} + +/* Settings for buttons. */ + +td.button +{ + background: #E0E0E0; + border: 1px outset #FFFFFF; + font-weight: bold; +} diff --git a/docs/manual/troubleshooting.html b/docs/manual/troubleshooting.html new file mode 100644 index 000000000..6b57aea1e --- /dev/null +++ b/docs/manual/troubleshooting.html @@ -0,0 +1,824 @@ + + + + + + +ProGuard Troubleshooting + + + + +

Troubleshooting

+ +While preparing a configuration for processing your code, you may bump into a +few problems. The following sections discuss some common issues and solutions: + +

Problems while processing

+ + +

Unexpected observations after processing

+ + +

Problems while converting to Android Dalvik bytecode

+ + + +

Problems while preverifying for Java Micro Edition

+ + + +

Problems at run-time

+ + + +

Problems while processing

+ +ProGuard may print out some notes and non-fatal warnings: + +
+
Note: can't find dynamically referenced class
+ +
ProGuard can't find a class or interface that your code is accessing by + means of introspection. You should check if you want to add the jar that + contains this class.
+ +
Note: ... calls '(...)Class.forName(variable).newInstance()'
+ +
ProGuard lists all class casts of dynamically created class instances, + like "(MyClass)Class.forName(variable).newInstance()". + Depending on your application, you may need to keep the mentioned classes + with an option like "-keep class MyClass", or their + implementations with an option like "-keep class * implements + MyClass". You can switch off these notes by specifying the + -dontnote option.
+ +
Note: ... accesses a field/method '...' dynamically
+ +
ProGuard lists a number of constructs like + ".getField("myField")". Depending on your application, you + may need to figure out where the mentioned class members are defined and + keep them with an option like "-keep class MyClass { MyFieldType + myField; }". Otherwise, ProGuard might remove or obfuscate the + class members, since it can't know which ones they are exactly. It does + list possible candidates, for your information. You can switch off these + notes by specifying the -dontnote option.
+ +
Note: the configuration keeps the entry point '...', but not the descriptor class '...'
+ +
Your configuration contains a -keep option to preserve the + given method (or field), but no -keep option for the given + class that is an argument type or return type in the method's descriptor. + You may then want to keep the class too. Otherwise, ProGuard will + obfuscate its name, thus changing the method's signature. The method might + then become unfindable as an entry point, e.g. if it is part of a public + API. You can switch off these notes by specifying the -dontnote option.
+ +
Note: the configuration doesn't specify which class members to keep for class '...'
+ +
Your configuration contains + a -keepclassmembers/-keepclasseswithmembers option to + preserve fields or methods in the given class, but it doesn't specify + which fields or methods. This way, the option simply won't have any + effect. You probably want to specify one or more fields or methods, as + usual between curly braces. You can specify all fields or methods with a + wildcard "*;". You can switch off these notes by specifying + the -dontnote option.
+ +
Note: duplicate definition of program/library class
+ +
Your program jars or library jars contain multiple definitions of the + listed classes. ProGuard continues processing as usual, only considering + the first definitions. The warning may be an indication of some problem + though, so it's advisable to remove the duplicates. A convenient way to do + so is by specifying filters on the input jars or library jars. You can + switch off these notes by specifying the -dontnote option.
+ +
Warning: can't write resource ... Duplicate zip entry
+ +
Your input jars contain multiple resource files with the same name. + ProGuard continues copying the resource files as usual, skipping any files + with previously used names. Once more, the warning may be an indication of + some problem though, so it's advisable to remove the duplicates. A + convenient way to do so is by specifying filters on the input jars. There + is no option to switch off these warnings.
+ +
+

+ +ProGuard may terminate when it encounters parsing errors or I/O errors, or +some more serious warnings: + +

+
Warning: can't find superclass or interface
Warning: can't find referenced class
+ +
A class in one of your program jars or library jars is referring to a + class or interface that is missing from the input. The output lists both + the referencing class(es) and the missing referenced class(es). There can + be a few reasons, with their own solutions: +

+

    +
  1. If the missing class is referenced from your own code, you may have + forgotten to specify an essential library. Just like when compiling + all code from scratch, you must specify all libraries that the code is + referencing, directly or indirectly. If the library should be + processed and included in the output, you should specify it with + -injars, otherwise you + should specify it with + -libraryjars. +

    + For example, if ProGuard complains that it can't find a + java.lang class, you have to make sure that you are + specifying the run-time library of your platform. For JSE, these are + typically packaged in lib/rt.jar (vm.jar for + IBM's JVM, and classes.jar in MacOS X). Other platforms + like JME and Android have their own run-time libraries. + The examples section provides more details + for the various platforms. +

    + If ProGuard still complains that it can't find a + javax.crypto class, you probably still have to specify + jce.jar, next to the more common rt.jar.

  2. +
  3. If the missing class is referenced from a pre-compiled third-party + library, and your original code runs fine without it, then the missing + dependency doesn't seem to hurt. The cleanest solution is to + filter out the referencing + class or classes from the input, with a filter like "-injars + myapplication.jar(!somepackage/SomeUnusedReferencingClass.class)". + ProGuard will then skip this class entirely in the input, and it will + not bump into the problem of its missing reference. However, you may + then have to filter out other classes that are in turn referencing the + removed class. In practice, this works best if you can filter out + entire unused packages at once, with a wildcard filter like + "-injars + myapplication.jar(!someunusedpackage/**)".

  4. +
  5. If you don't feel like filtering out the problematic classes, you can + try your luck with the -ignorewarnings + option, or even + the -dontwarn option. + Only use these options if you really know what you're doing though.
  6. +
+

+ This last solution is commonly necessary in the standard Android build + process. The standard build script automatically passes the libraries that + it can find in the libs directory to ProGuard. Unfortunately, + many third-party libraries refer to other libraries that are not actually + used and not present. This works fine in debug builds, but in release + builds, ProGuard expects all libraries, so it can perform a proper static + analysis. For example, if ProGuard complains that it can't find + a java.awt class, then some library that you are using is + referring to java.awt. This is a bit shady, since Android + doesn't have this package at all, but if your application works anyway, + you can let ProGuard accept it with "-dontwarn + java.awt.**".

+ +
Error: Can't find any super classes of ... (not even immediate super class ...)
Error: Can't find common super class of ... and ...
+ +
It seems like you tried to avoid the warnings from the previous paragraph + by specifying + -ignorewarnings + or -dontwarn, but it didn't + work out. ProGuard's optimization step and preverification step really + need the missing classes to make sense of the code. Preferably, you would + solve the problem by adding the missing library, as discussed. If you're + sure the class that references the missing class isn't used either, you + could also try filtering it out from the input, by adding a filter to the + corresponding -injars option: + "-injars + myapplication.jar(!somepackage/SomeUnusedClass.class)". As a final + solution, you could switch off optimization + (-dontoptimize) and + preverification + (-dontpreverify).
+ +
Warning: can't find referenced field/method
+ +
A class in one of your program jars is referring to a field or a method + that is missing from the input, even though its class is present. The + output lists both the referencing class and the missing referenced class + member. There can be a few reasons, with their own solutions: +

+

    +
  1. If there are unresolved references to class members in program + classes, your compiled class files are most likely inconsistent. + Possibly, some class file didn't get recompiled properly, or some + class file was left behind after its source file was removed. Try + removing all compiled class files and rebuilding your project.
  2. +
  3. If there are unresolved references to class members in library + classes, your compiled class files are inconsistent with the + libraries. You may need to recompile the class files, or otherwise + upgrade the libraries to consistent versions. +

    + For example, if you're developing for Android, and ProGuard complains + that it can't find a run-time method that is only available in recent + versions of Android, you should change the target in + project.properties to that recent version. +

    + Alternatively, you may get away with ignoring the inconsistency with + the options + -ignorewarnings + or even + -dontwarn. For + instance if the code contains a class to optionally support recent + versions of Android, you can specify "-dontwarn + mypackage.MySupportClass".

  4. +
  5. If your program classes reside in the same packages as library classes, + and refer to their package visible class members, then you should also + specify the + -dontskipnonpubliclibraryclassmembers + option.
  6. +
+ +
Warning: can't find enclosing class/method
+ +
If there are unresolved references to classes that are defined inside + methods in your input, once more, your compiled class files are most likely + inconsistent. Possibly, some class file didn't get recompiled properly, or + some class file was left behind after its source file was removed. Try + removing all compiled class files and rebuilding your project.
+ +
Warning: library class ... depends on program class ...
+ +
If any of your library classes depend on your program classes, by + extending, implementing or just referencing them, your processed code will + generally be unusable. Program classes can depend on library classes, but + not the other way around. Program classes are processed, while library + classes always remain unchanged. It is therefore impossible to adapt + references from library classes to program classes, for instance if the + program classes are renamed. You should define a clean separation between + program code (specified with -injars) and library code + (specified with -libraryjars), and try + again. +

+ On Android, it is not uncommon that sloppy libraries contain duplicates of + classes that are already present in the Android run-time + (e.g. org.json, org.xml, org.xmlpull, + org.w3c.dom, or org.apache.http). You must remove + these classes from your libraries, since they are possibly inconsistent, + and the run-time libraries would get precedence anyway.

+ +
Warning: class file ... unexpectedly contains class ...
+ +
The given class file contains a definition for the given class, but the + directory name of the file doesn't correspond to the package name of the + class. ProGuard will accept the class definition, but the current + implementation will not write out the processed version. Please make sure + your input classes are packaged correctly. Notably, class files that are + in the WEB-INF/classes directory in a war should be packaged + in a jar and put in the WEB-INF/lib directory. If you don't + mind these classes not being written to the output, you can specify the -ignorewarnings option, + or even the -dontwarn + option.
+ +
Warning: ... is not being kept as ..., but remapped to ...
+ +
There is a conflict between a -keep option in the + configuration, and the mapping file, in the obfuscation step. The given + class name or class member name can't be kept by its original name, as + specified in the configuration, but it has to be mapped to the other given + name, as specified in the mapping file. You should adapt your + configuration or your mapping file to remove the conflict. Alternatively, + if you're sure the renaming won't hurt, you can specify the -ignorewarnings option, + or even the -dontwarn + option.
+ +
Warning: field/method ... can't be mapped to ...
+ +
There is a conflict between some new program code and the mapping file, in + the obfuscation step. The given class member can't be mapped to the given + name, because it would conflict with another class member that is already + being mapped to the same name. This can happen if you are performing + incremental obfuscation, applying an obfuscation mapping file from an + initial obfuscation step. For instance, some new class may have been added + that extends two existing classes, introducing a conflict in the name + space of its class members. If you're sure the class member receiving + another name than the one specified won't hurt, you can specify the -ignorewarnings option, + or even the -dontwarn + option. Note that you should always use the -useuniqueclassmembernames + option in the initial obfuscation step, in order to reduce the risk of + conflicts.
+ +
Error: Unsupported class version number
+ +
You are trying to process class files compiled for a recent version of + Java that your copy of ProGuard doesn't support yet. You + should check + on-line if there is a more recent release.
+ +
Error: You have to specify '-keep' options
+ +
You either forgot to specify -keep options, or you mistyped the + class names. ProGuard has to know exactly what you want to keep: an + application, an applet, a servlet, a midlet,..., or any combination of + these. Without the proper seed specifications, ProGuard would shrink, + optimize, or obfuscate all class files away.
+ +
Error: Expecting class path separator ';' before 'Files\Java\...' (in Windows)
+ +
If the path of your run-time jar contains spaces, like in "Program Files", + you have to enclose it with single or double quotes, as explained in the + section on file names. This is actually + true for all file names containing special characters, on all + platforms.
+ +
Error: Can't read [.../lib/rt.jar] (No such file or directory) (in MacOS X)
+ +
In MacOS X, the run-time classes may be in a different place than on most + other platforms. You'll then have to adapt your configuration, replacing + the path <java.home>/lib/rt.jar by + <java.home>/../Classes/classes.jar.
+ +
Error: Can't read ...
+ +
ProGuard can't read the specified file or directory. Double-check that the + name is correct in your configuration, that the file is readable, and that + it is not corrupt. An additional message "Unexpected end of ZLIB input + stream" suggests that the file is truncated. You should then make sure + that the file is complete on disk when ProGuard starts (asynchronous + copying? unflushed buffer or cache?), and that it is not somehow + overwritten by ProGuard's own output.
+ +
Error: Can't write ...
+ +
ProGuard can't write the specified file or directory. Double-check that + the name is correct in your configuration and that the file is + writable.
+ +
Internal problem starting the ProGuard GUI (Cannot write XdndAware property) (in Linux)
+ +
In Linux, at least with Java 6, the GUI may not start properly, due to + Sun + Bug #7027598. The work-around at this time is to specify the JVM + option -DsuppressSwingDropSupport=true when running the + GUI.
+ +
+

+ +Should ProGuard crash while processing your application: + +

+
OutOfMemoryError
+ +
You can try increasing the heap size of the Java virtual machine (with the + usual -Xms and -Xmx options). You can also + reduce the amount of memory that ProGuard needs by removing unnecessary + library jars from your configuration, or by filtering out unused library + packages and classes. Remember that only classes or interfaces that are + extended or implemented by classes in your input jars are required.
+ +
StackOverflowError
+ +
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 -Xss option) + should help too. In practice however, the -Xss setting + doesn't have any effect on the main thread, due to Sun Bug + #4362291. As a result, this solution will only work when running + ProGuard in a different thread, e.g. from its GUI.
+ +
Unexpected error
+ +
ProGuard has encountered an unexpected condition, typically in the + optimization step. It may or may not recover. You should be able to avoid + it using the -dontoptimize option. In + any case, please report the problem, preferably with the simplest example + that causes ProGuard to crash.
+ +
Otherwise...
+ +
Maybe your class files are corrupt. See if recompiling them and trying + again helps. If not, please report the problem, preferably with the + simplest example that causes ProGuard to crash.
+ +
+

+ +

Unexpected observations after processing

+ +If ProGuard seems to run fine, but your processed code doesn't look right, +there might be a couple of reasons: + +
+
Disappearing classes
+ +
If you are working on Windows and it looks like some classes have + disappeared from your output, you should make sure you're not writing your + output class files to a directory (or unpacking the output jar). On + platforms with case-insensitive file systems, such as Windows, unpacking + tools often let class files with similar lower-case and upper-case names + overwrite each other. If you really can't switch to a different operating + system, you could consider using ProGuard's -dontusemixedcaseclassnames + option. +

+ Also, you should make sure your class files are in directories that + correspond to their package names. ProGuard will read misplaced class + files, but it will currently not write their processed versions. Notably, + class files that are in the WEB-INF/classes directory in a + war should be packaged in a jar and put in the WEB-INF/lib + directory.

+ +
Classes or class members not being kept
+ +
If ProGuard is not keeping the right classes or class members, make sure + you are using fully qualified class names. If the package name of some + class is missing, ProGuard won't match the elements that you might be + expecting. It may help to double-check for typos too. You can use the -printseeds option to see + which elements are being kept exactly. +

+ 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, with + an option like "-keep interface MyMarkerInterface". +

+ Similarly, if you are keeping classes based on annotations, you may have + to avoid that the annotation classes themselves are removed in the + shrinking step. You should package the annotation classes as a library, or + explicitly keep them in your program code with an option like "-keep + @interface *".

+ +
Variable names not being obfuscated
+ +
If the names of the local variables and parameters in your obfuscated code + don't look obfuscated, because they suspiciously resemble the names of + their types, it's probably because the decompiler that you are using is + coming up with those names. ProGuard's obfuscation step does remove the + original names entirely, unless you explicitly keep the + LocalVariableTable or LocalVariableTypeTable + attributes.
+ +
+ +

Problems while converting to Android Dalvik bytecode

+ +If ProGuard seems to run fine, but the dx tool in the Android SDK subsequently +fails with an error: + +
+
SimException: local variable type mismatch
+ +
This error indicates that ProGuard's optimization step has not been able + to maintain the correct debug information about local variables. This can + happen if some code is optimized radically. Possible work-arounds: let the + java compiler not produce debug information (-g:none), or let + ProGuard's obfuscation step remove the debug information again + (by not keeping the attributes LocalVariableTable + and LocalVariableTypeTable + with -keepattributes), + or otherwise just disable optimization + (-dontoptimize).
+ +
Conversion to Dalvik format failed with error 1
+ +
This error may have various causes, but if dx is tripping over some code + processed by ProGuard, you should make sure that you are using the latest + version of ProGuard. You can just copy the ProGuard jars + to android-sdk/tools/proguard/lib. If that doesn't help, + please report the problem, preferably with the simplest example that still + brings out the error.
+ +
+ +

Problems while preverifying for Java Micro Edition

+ +If ProGuard seems to run fine, but the external preverifier subsequently +produces errors, it's usually for a single reason: + +
+
InvalidClassException, class loading error, or verification error
+ +
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 + preverify tool always unpacks the jars, so class files with + similar lower-case and upper-case names overwrite each other. You can use + ProGuard's -dontusemixedcaseclassnames + option to work around this problem. +

+ 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 -dontoptimize 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.

+ +
+ +Note that it is no longer necessary to use an external preverifier. With the +-microedition option, +ProGuard will preverify the class files for Java Micro Edition. +

+ +

Problems at run-time

+ +If ProGuard runs fine, but your processed application doesn't work, there +might be several reasons: + +
+
Stack traces without class names or line numbers
+ +
If your stack traces don't contain any class names or lines numbers, + even though you are keeping the proper attributes, make sure this debugging + information is present in your compiled code to start with. Notably the Ant + javac task has debugging information switched off by default.
+ +
NoClassDefFoundError
+ +
Your class path is probably incorrect. It should at least contain all + library jars and, of course, your processed program jar.
+ +
ClassNotFoundException
+ +
Your code is probably calling Class.forName, trying to create + the missing class dynamically. ProGuard can only detect constant name + arguments, like Class.forName("mypackage.MyClass"). For + variable name arguments like Class.forName(someClass), you + have to keep all possible classes using the appropriate -keep option, e.g. "-keep + class mypackage.MyClass" or "-keep class * implements + mypackage.MyInterface".
+ +
NoSuchFieldException
+ +
Your code is probably calling something like + myClass.getField, trying to find some field dynamically. + Since ProGuard can't always detect this automatically, you have to keep + the missing field in using the + appropriate -keep option, e.g. + "-keepclassmembers class mypackage.MyClass { int myField; + }".
+ +
NoSuchMethodException
+ +
Your code is probably calling something like + myClass.getMethod, trying to find some method dynamically. + Since ProGuard can't always detect this automatically, you have to keep + the missing method in using the + appropriate -keep option, e.g. + "-keepclassmembers class mypackage.MyClass { void myMethod(); + }". +

+ More specifically, if the method reported as missing is + values or valueOf, you probably have to keep + some methods related to enumerations.

+ +
MissingResourceException or NullPointerException
+ +
Your processed code may be unable to find some resource files. ProGuard + simply copies resource files over from the input jars to the output jars. + Their names and contents remain unchanged, unless you specify the options + -adaptresourcefilenames + and/or -adaptresourcefilecontents. +

+ Furthermore, directory entries in jar files aren't copied, unless you + specify the option -keepdirectories. + Note that Sun advises against calling Class.getResource() for + directories (Sun + Bug #4761949).

+ +
Disappearing annotations
+ +
By default, the obfuscation step removes all annotations. If your + application relies on annotations to function properly, you should + explicitly keep them with + -keepattributes + *Annotation*.
+ +
Invalid or corrupt jarfile
+ +
You are probably starting your application with the java option + -jar instead of the option -classpath. The java + virtual machine returns with this error message if your jar doesn't + contain a manifest file (META-INF/MANIFEST.MF), if the + manifest file doesn't specify a main class (Main-Class: ...), + 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,
+ +
InvalidJarIndexException: Invalid index
+ +
At least one of your processed jar files contains an index file + META-INF/INDEX.LIST, listing all class files in the jar. + ProGuard by default copies files like these unchanged. ProGuard may however + remove or rename classes, thus invalidating the file. You should filter the + index file out of the input + (-injars in.jar(!META-INF/INDEX.LIST)) or update the file + after having applied ProGuard (jar -i out.jar). +
+ +
InvalidClassException, class loading error, or verification error (in Java Micro Edition)
+ +
If you get such an error in Java Micro Edition, you may have forgotten to + specify the -microedition option, so + the processed class files are preverified properly.
+ +
Error: No Such Field or Method, Error verifying method (in a Java Micro Edition emulator)
+ +
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 -repackageclasses + '' and -overloadaggressively. + If you're using the JME WTK plugin, you can adapt the configuration + proguard/wtk/default.pro that's inside the + proguard.jar.
+ +
Failing midlets (on a Java Micro Edition device)
+ +
If your midlet runs in an emulator and on some devices, but not on some + other devices, this is probably due to a bug in the latter devices. For + some older Motorola and Nokia phones, you might try specifying the -useuniqueclassmembernames + option. It avoids overloading class member names, which triggers a bug in + their java virtual machine. +

+ You might also try using the -dontusemixedcaseclassnames + option. Even if the midlet has been properly processed and then + preverified on a case-sensitive file system, the device itself might not + like the mixed-case class names. Notably, the Nokia N-Gage emulator works + fine, but the actual device seems to exhibit this problem.

+ +
Disappearing loops
+ +
If your code contains empty busy-waiting loops, ProGuard's optimization + step may remove them. More specifically, this happens if a loop + continuously checks the value of a non-volatile field that is changed in a + different thread. The specifications of the Java Virtual Machine require + that you always mark fields that are accessed across different threads + without further synchronization as volatile. If this is not + possible for some reason, you'll have to switch off optimization using the + -dontoptimize + option.
+ +
SecurityException: SHA1 digest error
+ +
You may have forgotten to sign your program jar after having + processed it with ProGuard.
+ +
ClassCastException: class not an enum, or
IllegalArgumentException: class not an enum type
+ +
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.
+ +
ArrayStoreException: sun.reflect.annotation.EnumConstantNotPresentExceptionProxy
+ +
You are probably processing annotations involving enumerations. Again, you + should make sure you're preserving the special methods of the enumeration + type, as shown in the examples.
+ +
CompilerError: duplicate addition
+ +
You are probably compiling or running some code that has been obfuscated + with the -overloadaggressively + option. This option triggers a bug in + sun.tools.java.MethodSet.add in Sun's JDK 1.2.2, which is + used for (dynamic) compilation. You should then avoid this option.
+ +
ClassFormatError: repetitive field name/signature
+ +
You are probably processing some code that has been obfuscated before with + the -overloadaggressively + option. You should then use the same option again in the second processing + round.
+ +
ClassFormatError: Invalid index in LocalVariableTable in class file
+ +
If you are keeping the LocalVariableTable or + LocalVariableTypeTable attributes, ProGuard's optimizing step + is sometimes unable to update them consistently. You should then let the + obfuscation step remove these attributes or disable the optimization + step.
+ +
NoSuchMethodError or AbstractMethodError
+ +
You should make sure you're not writing your output class files to a + directory on a platform with a case-insensitive file system, such as + Windows. Please refer to the section about disappearing classes for details. +

+ Furthermore, you should check whether you have specified your program jars + and library jars properly. Program classes can refer to library classes, + but not the other way around. +

+ If all of this seems ok, perhaps there's a bug in ProGuard (gasp!). If so, + please report it, preferably with the simplest example on which you can + find ProGuard to fail.

+ +
VerifyError
+ +
Verification errors when executing a program are almost certainly the + result of a bug in the optimization step of ProGuard. Make sure you are + using the latest version. You should be able to work around the problem by + using the -dontoptimize + option. You can check the bug database to see if it is a known problem + (often with a fix). Otherwise, please report it, preferably with the + simplest example on which ProGuard fails.
+ +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/usage.html b/docs/manual/usage.html new file mode 100644 index 000000000..7d3a5bb3d --- /dev/null +++ b/docs/manual/usage.html @@ -0,0 +1,1246 @@ + + + + + + +ProGuard Usage + + + + +

Usage

+ +To run ProGuard, just type: +

+java -jar proguard.jar options ... +

+You can find the ProGuard jar in the lib directory of the +ProGuard distribution. Alternatively, the bin directory contains +some short Linux and Windows scripts containing this command. Typically, you'll +put most options in a configuration file (say, myconfig.pro), and +just call: +

+java -jar proguard.jar @myconfig.pro +

+You can combine command line options and options from configuration files. For +instance: +

+java -jar proguard.jar @myconfig.pro -verbose +

+

+You can add comments in a configuration file, starting with a +# character and continuing until the end of the line. +

+Extra whitespace between words and delimiters is ignored. File names with +spaces or special characters should be quoted with single or double quotes. +

+Options can be grouped arbitrarily in arguments on the command line and in +lines in configuration files. This means that you can quote arbitrary sections +of command line options, to avoid shell expansion of special characters, for +instance. +

+The order of the options is generally irrelevant. For quick experiments, you +can abbreviate them to their first unique characters. +

+ +The sections below provide more details: +

+ +

Input/Output Options

+ +
+
@filename
+ +
Short for '-include + filename'.
+ +
-include + filename
+ +
Recursively reads configuration options from the given file + filename.
+ +
-basedirectory + directoryname
+ +
Specifies the base directory for all subsequent relative file names in + these configuration arguments or this configuration file.
+ +
-injars + class_path
+ +
Specifies the input jars (or wars, ears, zips, or directories) of the + application to be processed. The class files in these jars will be + processed and written to the output jars. By default, any non-class files + will be copied without changes. Please be aware of any temporary files + (e.g. created by IDEs), especially if you are reading your input files + straight from directories. The entries in the class path can be filtered, + as explained in the filters section. For better + readability, class path entries can be specified using multiple + -injars options.
+ +
-outjars + class_path
+ +
Specifies the names of the output jars (or wars, ears, zips, or + directories). The processed input of the preceding -injars + options will be written to the named jars. This allows you to collect the + contents of groups of input jars into corresponding groups of output jars. + In addition, the output entries can be filtered, as explained in + the filters section. Each processed class file + or resource file is then written to the first output entry with a matching + filter, within the group of output jars. +

+ You must avoid letting the output files overwrite any input files. For + better readability, class path entries can be specified using multiple + -outjars options. Without any -outjars options, + no jars will be written.

+ +
-libraryjars + class_path
+ +
Specifies the library jars (or wars, ears, zips, or directories) of the + application to be processed. The files in these jars will not be included + in the output jars. The specified library jars should at least contain the + class files that are extended by application class files. Library + class files that are only called needn't be present, although their + presence can improve the results of the optimization step. The entries in + the class path can be filtered, as explained in the filters section. For better readability, class path + entries can be specified using multiple -libraryjars options. +

+ Please note that the boot path and the class path set for running ProGuard + are not considered when looking for library classes. This means that you + explicitly have to specify the run-time jar that your code will use. + Although this may seem cumbersome, it allows you to process applications + targeted at different run-time environments. For example, you can process + J2SE applications as well as JME midlets or Android apps, just by + specifying the appropriate run-time jar.

+ +
-skipnonpubliclibraryclasses
+ +
Specifies to skip non-public classes while reading library jars, to speed + up processing and reduce memory usage of ProGuard. By default, ProGuard + reads non-public and public library classes alike. However, non-public + classes are often not relevant, if they don't affect the actual program + code in the input jars. Ignoring them then speeds up ProGuard, without + affecting the output. Unfortunately, some libraries, including recent JSE + run-time libraries, contain non-public library classes that are extended + by public library classes. You then can't use this option. ProGuard will + print out warnings if it can't find classes due to this option being + set.
+ +
-dontskipnonpubliclibraryclasses
+ +
Specifies not to ignore non-public library classes. As of version 4.5, this + is the default setting.
+ +
-dontskipnonpubliclibraryclassmembers
+ +
Specifies not to ignore package visible library class members (fields and + methods). By default, ProGuard skips these class members while parsing + library classes, as program classes will generally not refer to them. + Sometimes however, program classes reside in the same packages as library + classes, and they do refer to their package visible class members. In + those cases, it can be useful to actually read the class members, in order + to make sure the processed code remains consistent.
+ +
-keepdirectories + [directory_filter]
+ +
Specifies the directories to be kept in the output jars (or wars, ears, + zips, or directories). By default, directory entries are removed. This + reduces the jar size, but it may break your program if the code tries to + find them with constructs like + "mypackage.MyClass.class.getResource("")". You'll then want to + keep the directory corresponding to the package, "-keepdirectories + mypackage". If the option is specified without a filter, all + directories are kept. With a filter, only matching directories are + kept.
+ +
-target version
+ +
Specifies the version number to be set in the processed class files. The + version number can be one of 1.0, 1.1, + 1.2, 1.3, 1.4, 1.5 (or + just 5), 1.6 (or just 6), or + 1.7 (or just 7). By default, the version numbers + of the class files are left unchanged. For example, you may want to + upgrade class files to Java 6, by + changing their version numbers and having them preverified.
+ +
-forceprocessing
+ +
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.
+ +
+

+ +

Keep Options

+ +
+
-keep + [,modifier,...] + class_specification
+ +
Specifies classes and class members (fields and methods) to be preserved + as entry points to your code. For example, in order to keep an application, you can specify + the main class along with its main method. In order to process a library, you should specify all + publicly accessible elements.
+ +
-keepclassmembers + [,modifier,...] + class_specification
+ +
Specifies class members to be preserved, if their classes are preserved as + well. For example, you may want to keep all serialization fields and + methods of classes that implement the Serializable + interface.
+ +
-keepclasseswithmembers + [,modifier,...] + class_specification
+ +
Specifies classes and class members to be preserved, on the condition that + all of the specified class members are present. For example, you may want + to keep all applications that + have a main method, without having to list them explicitly.
+ +
-keepnames + class_specification
+ +
Short for -keep,allowshrinking + class_specification +

+ 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 + keep all class names of classes + that implement the Serializable interface, so that the + processed code remains compatible with any originally serialized classes. + Classes that aren't used at all can still be removed. Only applicable when + obfuscating.

+ +
-keepclassmembernames + class_specification
+ +
Short for -keepclassmembers,allowshrinking + class_specification +

+ 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 class$ methods + when processing a library compiled by + JDK 1.2 or older, so obfuscators can detect it again when processing an + application that uses the processed library (although ProGuard itself + doesn't need this). Only applicable when obfuscating.

+ +
-keepclasseswithmembernames + class_specification
+ +
Short for -keepclasseswithmembers,allowshrinking + class_specification +

+ 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 keep all native method names and the names + of their classes, so that the processed code can still link with the + native library code. Native methods that aren't used at all can still be + removed. If a class file is used, but none of its native methods are, its + name will still be obfuscated. Only applicable when obfuscating.

+ +
-printseeds + [filename]
+ +
Specifies to exhaustively list classes and class members matched by the + various -keep options. The list is printed to the standard + output or to the given file. The list can be useful to verify if the + intended class members are really found, especially if you're using + wildcards. For example, you may want to list all the applications or all the applets that you are keeping.
+ +
+

+ +

Shrinking Options

+ +
+
-dontshrink
+ +
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 -keep options, and the ones on which + 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.
+ +
-printusage + [filename]
+ +
Specifies to list dead code of the input class files. The list is printed + to the standard output or to the given file. For example, you can list the unused code of an application. + Only applicable when shrinking.
+ +
-whyareyoukeeping + class_specification
+ +
Specifies to print details on why the given classes and class members are + being kept in the shrinking step. This can be useful if you are wondering + why some given element is present in the output. In general, there can be + many different reasons. This option prints the shortest chain of methods + to a specified seed or entry point, for each specified class and class + member. In the current implementation, the shortest chain that is + printed out may sometimes contain circular deductions -- these do not + reflect the actual shrinking process. If the -verbose option if specified, the traces + include full field and method signatures. Only applicable when + shrinking.
+ +
+

+ +

Optimization Options

+ +
+
-dontoptimize
+ +
Specifies not to optimize the input class files. By default, optimization + is enabled; all methods are optimized at a bytecode level.
+ +
-optimizations + optimization_filter
+ +
Specifies the optimizations to be enabled and disabled, at a more + fine-grained level. Only applicable when optimizing. This is an expert + option.
+ +
-optimizationpasses n
+ +
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.
+ +
-assumenosideeffects + class_specification
+ +
Specifies methods that don't have any side effects (other than maybe + returning a value). In the optimization step, ProGuard will then remove + calls to such methods, if it can determine that the return values aren't + used. ProGuard will analyze your program code to find such methods + automatically. It will not analyze library code, for which this option can + therefore be useful. For example, you could specify the method + System.currentTimeMillis(), so that any idle calls to it will + be removed. With some care, you can also use the option to + remove logging code. 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. Only use + this option if you know what you're doing!
+ +
-allowaccessmodification
+ +
Specifies that the access modifiers of classes and class members may be + broadened during processing. This can improve the results of the + optimization step. For instance, when inlining a public getter, it may be + necessary to make the accessed field public too. Although Java's binary + compatibility specifications formally do not require this (cfr. The Java Language Specification, Second Edition, Section 13.4.6), some virtual machines would have problems with the + processed code otherwise. Only applicable when optimizing (and when + obfuscating with the -repackageclasses option). +

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

+ +
-mergeinterfacesaggressively
+ +
Specifies that interfaces may be merged, even if their implementing + classes don't implement all interface methods. This can reduce the size of + the output by reducing the total number of classes. Note that Java's + binary compatibility specifications allow such constructs (cfr. The Java Language Specification, Second Edition, Section 13.5.3), even if they are not allowed in the Java language + (cfr. The Java Language Specification, Second Edition, Section 8.1.4). Only applicable when optimizing. +

+ Counter-indication: setting this option can reduce the performance + of the processed code on some JVMs, since advanced just-in-time + compilation tends to favor more interfaces with fewer implementing + classes. Worse, some JVMs may not be able to handle the resulting code. + Notably: +

    +
  • Sun's JRE 1.3 may throw an InternalError when + encountering more than 256 Miranda methods (interface methods + without implementations) in a class.
  • +
+ +
+

+ +

Obfuscation Options

+ +
+
-dontobfuscate
+ +
Specifies not to obfuscate the input class files. By default, obfuscation + is applied; classes and class members receive new short random names, + except for the ones listed by the various -keep options. + Internal attributes that are useful for debugging, such as source files + names, variable names, and line numbers are removed.
+ +
-printmapping + [filename]
+ +
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. For example, it is required for + subsequent incremental + obfuscation, or if you ever want to make sense again of obfuscated stack traces. Only + applicable when obfuscating.
+ +
-applymapping + filename
+ +
Specifies to reuse the given name mapping that was printed out in a + previous obfuscation run of ProGuard. Classes and class members that are + listed in the mapping file receive the names specified along with them. + Classes and class members that are not mentioned receive new names. The + mapping may refer to input classes as well as library classes. This option + can be useful for incremental + obfuscation, i.e. processing add-ons or small patches to an existing + piece of code. If the structure of the code changes fundamentally, + ProGuard may print out warnings that applying a mapping is causing + conflicts. You may be able to reduce this risk by specifying the option -useuniqueclassmembernames + in both obfuscation runs. Only a single mapping file is allowed. Only + applicable when obfuscating.
+ +
-obfuscationdictionary + filename
+ +
Specifies a text file from which all valid words are used as obfuscated + 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 # 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.
+ +
-classobfuscationdictionary + filename
+ +
Specifies a text file from which all valid words are used as obfuscated + class names. The obfuscation dictionary is similar to the one of the + option -obfuscationdictionary. + Only applicable when obfuscating.
+ +
-packageobfuscationdictionary + filename
+ +
Specifies a text file from which all valid words are used as obfuscated + package names. The obfuscation dictionary is similar to the one of the + option -obfuscationdictionary. + Only applicable when obfuscating.
+ +
-overloadaggressively
+ +
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 processed code even smaller (and less comprehensible). Only + applicable when obfuscating. +

+ Counter-indication: the resulting class files fall within the Java + bytecode specification (cfr. The Java Virtual Machine Specification, Second Edition, first + paragraphs of Section 4.5 and Section 4.6), even though this kind of overloading is not allowed in + the Java language (cfr. The Java Language Specification, Second Edition, Section 8.3 and Section 8.4.7). Still, some tools have problems with it. Notably: +

    +
  • Sun's JDK 1.2.2 javac compiler produces an exception when + compiling with such a library (cfr. Bug #4216736). + You probably shouldn't use this option for processing libraries.
  • +
  • Sun's JRE 1.4 and later fail to serialize objects with overloaded + primitive fields.
  • +
  • Sun's JRE 1.5 pack200 tool reportedly has problems with + overloaded class members.
  • +
  • Google's Dalvik VM can't handle overloaded static fields.
  • +
+ +
-useuniqueclassmembernames
+ +
Specifies to assign the same obfuscated names to class members that have + the same names, and different obfuscated names to class members that have + different names (for each given class member signature). Without the + option, more class members can be mapped to the same short names like 'a', + 'b', etc. The option therefore increases the size of the resulting code + slightly, but it ensures that the saved obfuscation name mapping can + always be respected in subsequent incremental obfuscation steps. +

+ For instance, consider two distinct interfaces containing methods with the + same name and signature. Without this option, these methods may get + different obfuscated names in a first obfuscation step. If a patch is then + added containing a class that implements both interfaces, ProGuard will + have to enforce the same method name for both methods in an incremental + obfuscation step. The original obfuscated code is changed, in order to + keep the resulting code consistent. With this option in the initial + obfuscation step, such renaming will never be necessary. +

+ 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 steps could remove or + modify parts of your code that are essential for later additions.

+ +
-dontusemixedcaseclassnames
+ +
Specifies not to generate mixed-case class names while obfuscating. By + default, obfuscated class names can contain a mix of upper-case characters + and lower-case characters. This creates perfectly acceptable and usable + jars. Only if a jar is unpacked on a platform with a case-insensitive + filing system (say, Windows), the unpacking tool may let similarly named + class files overwrite each other. Code that self-destructs when it's + unpacked! Developers who really want to unpack their jars on Windows can + use this option to switch off this behavior. Obfuscated jars will become + slightly larger as a result. Only applicable when obfuscating.
+ +
-keeppackagenames + [package_filter]
+ +
Specifies not to obfuscate the given package names. The optional filter is + a comma-separated list of package names. Package names can contain + ?, *, and ** wildcards, and they can be preceded by + the ! negator. Only applicable when obfuscating.
+ +
-flattenpackagehierarchy + [package_name]
+ +
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 obfuscating package + names. It can make the processed code smaller and less comprehensible. + Only applicable when obfuscating.
+ +
-repackageclasses + [package_name]
+ +
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 + -flattenpackagehierarchy + option. It is another example of further obfuscating package names. It can + make the processed code even smaller and less comprehensible. Its + deprecated name is -defaultpackage. Only applicable when + obfuscating. +

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

+ +
-keepattributes + [attribute_filter]
+ +
Specifies any optional attributes to be preserved. The attributes can be + specified with one or more -keepattributes directives. The + optional filter is a comma-separated list of attribute names. Attribute + names can contain ?, *, and ** wildcards, and they + can be preceded by the ! negator. Typical optional attributes are + Exceptions, Signature, Deprecated, + SourceFile, SourceDir, + LineNumberTable, LocalVariableTable, + LocalVariableTypeTable, Synthetic, + EnclosingMethod, RuntimeVisibleAnnotations, + RuntimeInvisibleAnnotations, + RuntimeVisibleParameterAnnotations, + RuntimeInvisibleParameterAnnotations, and + AnnotationDefault. The InnerClasses attribute + name can be specified as well, referring to the source name part of this + attribute. For example, you should at least keep the + Exceptions, InnerClasses, and + Signature attributes + when processing a library. You should + also keep the SourceFile and + LineNumberTable attributes + for producing useful obfuscated stack + traces. Finally, you may want + to keep annotations if your code + depends on them. Only applicable when obfuscating.
+ +
-keepparameternames
+ +
Specifies to keep the parameter names and types of methods that are kept. + This option actually keeps trimmed versions of the debugging attributes + LocalVariableTable and + LocalVariableTypeTable. It can be useful when + processing a library. Some IDEs can + use the information to assist developers who use the library, for example + with tool tips or autocompletion. Only applicable when obfuscating.
+ +
-renamesourcefileattribute + [string]
+ +
Specifies a constant string to be put in the SourceFile + attributes (and SourceDir attributes) of the class files. + Note that the attribute has to be present to start with, so it also has to + be preserved explicitly using the -keepattributes directive. + For example, you may want to have your processed libraries and + applications produce useful obfuscated + stack traces. Only applicable when obfuscating.
+ +
-adaptclassstrings + [class_filter]
+ +
Specifies that string constants that correspond to class names should be + obfuscated as well. Without a filter, all string constants that correspond + to class names are adapted. With a filter, only string constants in + classes that match the filter are adapted. For example, if your code + contains a large number of hard-coded strings that refer to classes, and + you prefer not to keep their names, you may want to use this option. + Primarily applicable when obfuscating, although corresponding classes are + automatically kept in the shrinking step too.
+ +
-adaptresourcefilenames + [file_filter]
+ +
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 processing resource files. Only + applicable when obfuscating.
+ +
-adaptresourcefilecontents + [file_filter]
+ +
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 classes (if any). Without a filter, the + contents of all resource files updated. With a filter, only matching files + are updated. The resource files are parsed and written using the + platform's default character set. You can change this default character set + by setting the environment variable LANG or the Java system + property file.encoding. For an example, + see processing resource files. + Only applicable when obfuscating.
+ +
+

+ +

Preverification Options

+ +
+
-dontpreverify
+ +
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 optional, but as of + Java 7, it is required. Only when eventually targeting Android, it is not + necessary, so you can then switch it off to reduce the processing time a + bit.
+ +
-microedition
+ +
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 processing midlets.
+ +
+

+ +

General Options

+ +
+
-verbose
+ +
Specifies to write out some more information during processing. If the + program terminates with an exception, this option will print out the entire + stack trace, instead of just the exception message.
+ +
-dontnote + [class_filter]
+ +
Specifies not to print notes about potential mistakes or omissions in the + configuration, like typos in class names, or like missing options that + might be useful. The optional filter is a regular expression; ProGuard + doesn't print notes about classes with matching names.
+ +
-dontwarn + [class_filter]
+ +
Specifies not to warn about unresolved references and other important + problems at all. The optional filter is a regular expression; ProGuard + doesn't print warnings about classes with matching names. Ignoring + warnings can be dangerous. For instance, if the unresolved classes or + class members are indeed required for processing, the processed code will + not function properly. Only use this option if you know what you're + doing!
+ +
-ignorewarnings
+ +
Specifies to print any warnings about unresolved references and other + important problems, but to continue processing in any case. Ignoring + warnings can be dangerous. For instance, if the unresolved classes or + class members are indeed required for processing, the processed code will + not function properly. Only use this option if you know what you're + doing!
+ +
-printconfiguration + [filename]
+ +
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.
+ +
-dump + [filename]
+ +
Specifies to write out the internal structure of the class files, after + any processing. The structure is printed to the standard output or to the + given file. For example, you may want to write out the contents of a given jar + file, without processing it at all.
+ +
+

+ +

Class Paths

+ +ProGuard accepts a generalization of class paths to specify input files and +output files. A class path consists of entries, separated by the traditional +path separator (e.g. ':' on Unix, or ';' on Windows platforms). +The order of the entries determines their priorities, in case of duplicates. +

+Each input entry can be: +

    +
  • A class file or resource file,
  • +
  • A jar file, containing any of the above,
  • +
  • A war file, containing any of the above,
  • +
  • An ear file, containing any of the above,
  • +
  • A zip file, containing any of the above,
  • +
  • A directory (structure), containing any of the above.
  • +
+

+The paths of directly specified class files and resource files is ignored, so +class files should generally be part of a jar file, a war file, an ear file, a +zip file, or a directory. In addition, the paths of class files should not have +any additional directory prefixes inside the archives or directories. + +

+Each output entry can be: +

    +
  • A jar file, in which all processed class files and resource files will be + collected.
  • +
  • A war file, in which any and all of the above will be collected,
  • +
  • An ear file, in which any and all of the above will be collected,
  • +
  • A zip file, in which any and all of the above will be collected,
  • +
  • A directory, in which any and all of the above will be collected.
  • +
+

+When writing output entries, ProGuard will generally package the results in a +sensible way, reconstructing the input entries as much as required. Writing +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. The Examples section shows a few ways +to restructure output archives. +

+Files and directories can be specified as discussed in the section on file names below. +

+In addition, ProGuard provides the possibility to filter the class path +entries and their contents, based on their full relative file names. Each +class path entry can be followed by up to 5 types of file filters between parentheses, separated by +semi-colons: +

    +
  • A filter for all zip names that are encountered,
  • +
  • A filter for all ear names that are encountered,
  • +
  • A filter for all war names that are encountered,
  • +
  • A filter for all jar names that are encountered,
  • +
  • A filter for all class file names and resource file names that are + encountered.
  • +
+

+If fewer than 5 filters are specified, they are assumed to be the latter +filters. Any empty filters are ignored. More formally, a filtered class path +entry looks like this: +

+classpathentry([[[[zipfilter;]earfilter;]warfilter;]jarfilter;]filefilter)
+
+

+Square brackets "[]" mean that their contents are optional. +

+For example, "rt.jar(java/**.class,javax/**.class)" matches all +class files in the java and javax directories inside +the rt jar. +

+For example, "input.jar(!**.gif,images/**)" matches all files in +the images directory inside the input jar, except +gif files. +

+The different filters are applied to all corresponding file types, irrespective +of their nesting levels in the input; they are orthogonal. +

+For example, +"input.war(lib/**.jar,support/**.jar;**.class,**.gif)" only +considers jar files in the lib and support +directories in the input war, not any other jar files. It then +matches all class files and gif files that are encountered. +

+The filters allow for an almost infinite number of packaging and repackaging +possibilities. The Examples section provides a few more examples +for filtering input and output. +

+ +

File Names

+ +ProGuard accepts absolute paths and relative paths for the various file names +and directory names. A relative path is interpreted as follows: +
    +
  • relative to the base directory, if set, or otherwise
  • +
  • relative to the configuration file in which it is specified, if any, or + otherwise
  • +
  • relative to the working directory.
  • +
+

+The names can contain Java system properties (or Ant properties, when using +Ant), delimited by angular brackets, '<' and '>'. The +properties are automatically replaced by their corresponding values. +

+For example, <java.home>/lib/rt.jar is automatically +expanded to something like /usr/local/java/jdk/jre/lib/rt.jar. +Similarly, <user.home> is expanded to the user's home +directory, and <user.dir> is expanded to the current +working directory. +

+Names with special characters like spaces and parentheses must be quoted with +single or double quotes. Each file name in a list of names has to be quoted +individually. Note that the quotes themselves may need to be escaped when used +on the command line, to avoid them being gobbled by the shell. +

+For example, on the command line, you could use an option like '-injars +"my program.jar":"/your directory/your program.jar"'. +

+ +

File Filters

+ +Like general filters, a file filter is a +comma-separated list of file names that can contain wildcards. Only files with +matching file names are read (in the case of input jars), or written (in the +case of output jars). The following wildcards are supported: + + + + + + + + +
?matches any single character in a file name.
*matches any part of a filename not containing the directory + separator.
**matches any part of a filename, possibly containing any number of + directory separators.
+ +For example, "java/**.class,javax/**.class" matches all +class files in the java and javax. +

+ +Furthermore, a file name can be preceded by an exclamation mark '!' to +exclude the file name from further attempts to match with +subsequent file names. +

+For example, "!**.gif,images/**" matches all files in the +images directory, except gif files. +

+The Examples section provides a few more examples for filtering input and output. + +

Filters

+ +ProGuard offers options with filters for many different aspects of the +configuration: names of files, directories, classes, packages, attributes, +optimizations, etc. +

+A filter is a list of comma-separated names that can contain wildcards. Only +names that match an item on the list pass the filter. The supported wildcards +depend on the type of names for which the filter is being used, but the +following wildcards are typical: + + + + + + + + +
?matches any single character in a name.
*matches any part of a name not containing the package separator or + directory separator.
**matches any part of a name, possibly containing any number of + package separators or directory separators.
+ +For example, "foo,*bar" matches the name foo and +all names ending with bar. +

+ +Furthermore, a name can be preceded by a negating exclamation mark '!' +to exclude the name from further attempts to match +with subsequent names. So, if a name matches an item in the filter, it +is accepted or rejected right away, depending on whether the item has a +negator. If the name doesn't match the item, it is tested against the next +item, and so on. It if doesn't match any items, it is accepted or rejected, +depending on the whether the last item has a negator or not. +

+For example, "!foobar,*bar" matches all names ending with +bar, except foobar. +

+ +

Overview of Keep Options

+ +The various -keep options for shrinking and obfuscation may seem +a bit confusing at first, but there's actually a pattern behind them. The +following table summarizes how they are related: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeepFrom being removed or renamedFrom being renamed
Classes and class members-keep-keepnames
Class members only-keepclassmembers-keepclassmembernames
Classes and class members, if class members present-keepclasseswithmembers-keepclasseswithmembernames
+

+ +Each of these -keep options is of course followed by a +specification of the classes and class +members (fields and methods) to which it should be applied. +

+If you're not sure which option you need, you should probably simply use +-keep. It will make sure the specified classes and class members +are not removed in the shrinking step, and not renamed in the obfuscation step. +

+attention +

    +
  • If you specify a class, without class members, ProGuard only preserves the + class and its parameterless constructor as entry points. It may + still remove, optimize, or obfuscate its other class members.
  • +
  • If you specify a method, ProGuard only preserves the method as an entry + point. Its code may still be optimized and adapted.
  • +
+

+ +

Keep Option Modifiers

+ +
+
allowshrinking
+ +
Specifies that the entry points specified in the -keep + 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.
+ +
allowoptimization
+ +
Specifies that the entry points specified in the -keep + 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.
+ +
allowobfuscation
+ +
Specifies that the entry points specified in the -keep + 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.
+ +
+

+ +

Class Specifications

+ +A class specification is a template of classes and class members (fields and +methods). It is used in the various -keep options and in the +-assumenosideeffects option. The corresponding option is only +applied to classes and class members that match the template. +

+The template was designed to look very Java-like, with some extensions for +wildcards. To get a feel for the syntax, you should probably look at the examples, but this is an attempt at a complete formal +definition: +

+ +

+[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname
+    [extends|implements [@annotationtype] classname]
+[{
+    [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |
+                                                                      (fieldtype fieldname);
+    [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |
+                                                                                           <init>(argumenttype,...) |
+                                                                                           classname(argumenttype,...) |
+                                                                                           (returntype methodname(argumenttype,...));
+    [@annotationtype] [[!]public|private|protected|static ... ] *;
+    ...
+}]
+
+

+Square brackets "[]" mean that their contents are optional. Ellipsis dots +"..." mean that any number of the preceding items may be specified. A vertical +bar "|" delimits two alternatives. Non-bold parentheses "()" just group parts +of the specification that belong together. The indentation tries to clarify +the intended meaning, but white-space is irrelevant in actual configuration +files. +

+

    + +
  • The class keyword refers to any interface or class. + The interface keyword restricts matches to interface + classes. The enum keyword restricts matches to + enumeration classes. Preceding the interface or + enum keywords by a ! restricts + matches to classes that are not interfaces or enumerations, + respectively.
  • + +
  • Every classname must be fully qualified, e.g. + java.lang.String. Inner classes are separated by a dollar sign + "$", e.g. java.lang.Thread$State. Class names + may be specified as regular + expressions containing the following wildcards: + + + + + + + + + + + + + + + +
    ?matches any single character in a class name, but not the package + separator. For example, "mypackage.Test?" matches + "mypackage.Test1" and "mypackage.Test2", but not + "mypackage.Test12".
    *matches any part of a class name not containing the package separator. For + example, "mypackage.*Test*" matches + "mypackage.Test" and + "mypackage.YourTestApplication", but not + "mypackage.mysubpackage.MyTest". Or, more generally, + "mypackage.*" matches all classes in + "mypackage", but not in its subpackages.
    **matches any part of a class name, possibly containing any number of + package separators. For example, "**.Test" matches all + Test classes in all packages except the root package. Or, + "mypackage.**" matches all classes in + "mypackage" and in its subpackages.
    + + For additional flexibility, class names can actually be comma-separated + lists of class names, with optional ! negators, just + like file name filters. This notation doesn't look very Java-like, so it + should be used with moderation. +

    + For convenience and for backward compatibility, the class name + * refers to any class, irrespective of its package.

  • + +
  • The extends and implements + specifications are typically used to restrict classes with wildcards. They + are currently equivalent, specifying that only classes extending or + implementing the given class qualify. Note that the given class itself is + not included in this set. If required, it should be specified in a + separate option.
  • + +
  • The @ specifications can be used to restrict classes + and class members to the ones that are annotated with the specified + annotation types. An annotationtype is specified just like a + classname.
  • + +
  • Fields and methods are specified much like in Java, except that method + argument lists don't contain argument names (just like in other tools + like javadoc and javap). The specifications can + also contain the following catch-all wildcards: + + + + + + + + + + + + + + + +
    <init>matches any constructor.
    <fields>matches any field.
    <methods>matches any method.
    *matches any field or method.
    + + Note that the above wildcards don't have return types. Only the + <init> wildcard has an argument list. +

    + + Fields and methods may also be specified using regular expressions. Names + can contain the following wildcards: + + + + + + +
    ?matches any single character in a method name.
    *matches any part of a method name.
    + + Types in descriptors can contain the following wildcards: + + + + + + + + + + + + + + + +
    %matches any primitive type ("boolean", "int", + etc, but not "void").
    ?matches any single character in a class name.
    *matches any part of a class name not containing the package separator.
    **matches any part of a class name, possibly containing any number of + package separators.
    ***matches any type (primitive or non-primitive, array or + non-array).
    ...matches any number of arguments of any type.
    + + Note that the ?, *, and ** + wildcards will never match primitive types. Furthermore, only the + *** wildcards will match array types of any dimension. For + example, "** get*()" matches "java.lang.Object + getObject()", but not "float getFloat()", nor + "java.lang.Object[] getObjects()".

  • + +
  • Constructors can also be specified using their short class names (without + package) or using their full class names. As in the Java language, the + constructor specification has an argument list, but no return type.
  • + +
  • The class access modifiers and class member access modifiers are typically + used to restrict wildcarded classes and class members. They specify that + the corresponding access flags have to be set for the member to match. A + preceding ! specifies that the corresponding access + flag should be unset. +

    + Combining multiple flags is allowed (e.g. public static). It + means that both access flags have to be set (e.g. public + and static), except when they are conflicting, in + which case at least one of them has to be set (e.g. at least + public + or protected). +

    + ProGuard supports the additional modifiers synthetic, + bridge, and varargs, which may be + set by compilers.

  • + +
+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/manual/wtk.html b/docs/manual/wtk.html new file mode 100644 index 000000000..a7ea0b81b --- /dev/null +++ b/docs/manual/wtk.html @@ -0,0 +1,70 @@ + + + + + + +ProGuard JME Wireless Toolkit Integration + + + + +

JME Wireless Toolkit Integration

+ +ProGuard can be seamlessly integrated in Oracle's Wireless Toolkit (WTK) +for Java Micro Edition (JME). +

+ +The WTK already comes with a plug-in for ProGuard. Alternatively, ProGuard +offers its own plug-in. This latter implementation is recommended, as it more +up to date and it solves some problems. It is also somewhat more efficient, +invoking the ProGuard engine directly, instead of writing out a configuration +file and running ProGuard in a separate virtual machine. +

+ +In order to integrate this plug-in in the toolkit, you'll have to put the +following lines in the file +{j2mewtk.dir}/wtklib/Linux/ktools.properties or +{j2mewtk.dir}\wtklib\Windows\ktools.properties (whichever is +applicable). +

+ +

+obfuscator.runner.class.name: proguard.wtk.ProGuardObfuscator
+obfuscator.runner.classpath: /usr/local/java/proguard/lib/proguard.jar
+
+

+ +Please make sure the class path is set correctly for your system. +

+ +Once ProGuard has been set up, you can apply it to your projects as part of +the build process. The build process is started from the WTK menu bar: +

+

Project -> Package -> Create Obfuscated Package
+

+This option will compile, shrink, obfuscate, verify, and install your midlets +for testing. +

+Should you ever need to customize your ProGuard configuration for the JME WTK, +you can adapt the configuration file proguard/wtk/default.pro +that's inside the proguard.jar. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/quality.html b/docs/quality.html new file mode 100644 index 000000000..d9935fc41 --- /dev/null +++ b/docs/quality.html @@ -0,0 +1,56 @@ + + + + + + +ProGuard Quality + + + + +

Quality

+ +In order to get a feel for the quality of the ProGuard code, it is run +through a regular automatic build process. This process produces numerous +statistics on the source code, Java lint comments, Java documentation +comments, the Java documentation itself, html lint comments on the Java +documentation, spell checks, compilation results, an output jar, dead code +analysis, a shrunk and obfuscated jar (using ProGuard itself!), test runs with +memory and performance analyses, etc. Most analyses are produced using freely +available tools. The results are poured into a convenient set of web pages +using bash/sed/awk scripts. You're welcome to have a look at an uploaded +snapshot of one of these runs: +

+

Automated Code Analysis and Testing Pages (at SourceForge)
+

+The pages will appear in a new window, which you probably want to view at +full-screen size. +

+ +In addition, ProGuard is tested against a constantly growing test suite +(more than 1500 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. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/results.html b/docs/results.html new file mode 100644 index 000000000..eee8aa6a4 --- /dev/null +++ b/docs/results.html @@ -0,0 +1,169 @@ + + + + + + +ProGuard Results + + + + +

Results

+ +ProGuard 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: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Input ProgramOriginal sizeAfter shrinkingAfter optim.After obfusc.Total reductionTimeMemory usage
Worm, a sample midlet from Oracle's JME10.3 K9.8 K9.6 K8.5 K18 %2 s19 M
Javadocking, a docking library290 K281 K270 K201 K30 %12 s32 M
ProGuard itself648 K579 K557 K348 K46 %28 s66 M
JDepend, a Java quality metrics tool57 K36 K33 K28 K51 %6 s24 M
the run-time classes from Oracle's Java 653 M23 M22 M18 M66 %16 min270 M
Tomcat, the Apache servlet container1.1 M466 K426 K295 K74 %17 s44 M
JavaNCSS, a Java source metrics tool632 K242 K212 K152 K75 %20 s36 M
Ant, the Apache build tool2.4 M401 K325 K242 K90 %23 s61 M
+

+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. All of this technology +and software has evolved since, but the gist of the results remains the same. +

+The program sizes include companion libraries. The shrinking step produces the +best results for programs that use only small parts of their libraries. The +obfuscation step can significantly shrink large programs even further, since +the identifiers of their many internal references can be replaced by short +identifiers. +

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

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

+Memory usage (the amount of physical memory used by ProGuard while processing) +is governed by the basic java virtual machine and by the total size of the +library jars and program jars. + +


+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/saikoalogo.png b/docs/saikoalogo.png new file mode 100644 index 0000000000000000000000000000000000000000..5b34172157cc0f0efd05aefc4489cf4803048cb6 GIT binary patch literal 2901 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�}%?Rx8V7r zBZZdS8Avrag4hZ(su-Q8_=)KixJRbQQYV%=vZ9H=N9nD74Ug&C4dok}L>WT+Mci z&EPyw+Puxw{EfVdb%Pkr>P!sMOvl}9&KuRMDr=|fT+heQ&RA^60<2H=+|Nlk&XQct zyQnCa82*YO_`|z%r+4Yk4DA5;T)_fePkhL~_2+DkY@ZU`(3|_v+)T+U94L+=Hd;pnxWg%_tqwQ=-TytS#C%j4T|}4L#HEJkSm7!v7Y=6}WEX zIk-HnxjwDW)#}QD3OX|8-3?#jGlGb+p&seOQ`MfZ?!qp=)0F<5AOkFR&xGXA&JE>@@c3N`P z{%SyUYIv4Yu+S>X%<7%s+EL!cD5CtAf_H*mrqJ5Ts=Uq0s(iNsl-rH;oqns90UFw% z0?;%)9~OAp7J9@OU7J2dmH4W!5-ZX?)TB10pA7jdU^u)ht+w9gX3My?I*s1zSCaR_ zkO9n+^1P4GO~2Bu*R>J9++xOLe4}9tz+zmYp?2Q>vkSr~l4vW`P>aURcA?tspae|3 z6+N*H3a0kGsF7?4T0PzUeU>geL@?{f5>AZpZGtEYYKWYk{d#>wEqro0UTg}Mo9xG; zHN|XNgV8D$9yH=Zyr(kBx`4^n-mKnAOGNHS(oss_`?BN=dXkt1e>)_|QU2imDxBl_ zrsW|mn-W@wLexV*sfW2~7VA=8B&iF5b2(j!mAx-0h_;>E>EW zTkdVscfPXoHb-oWjRF_vLTKhw1(833$^ozmPxg=NaiLt^yKQ8tZE_YDcx@Sjv^$D; zddZDMzKk1qvCK|V-CPV;>3g@nqF&?R7)#bq&{1n)TcI$La z%)0)oyx!}0JwE3&(vA$!zOd7N?F3Qm?o7#^io2De?0<_& z*&B=4PB+?K*k|49EQ`EWXIf?E*G>oTE{^5p4jktW(Z~MHEeh<+`-bOQhVWHjPoh?{Vqb#?x7)O+dBH+o2sXn`;Nb#G#>PFJOx?M48Y~12nu4hYTHaqan zQSd2!@Kc=NhFpwDKF>kDo_s#u10IoXA}Msv;MU#dC)}|7R<_R9RQkf~yk70dF5mY_ zuQY3a<;cY(48bcOk?wte3eL4+-lK3#?+6+KmTc7WUb`p{;zxh9S?{0{`F%}Kv?%+b z2oJ{d*;GS4p7JN-9tU1E{$vY*fL~9`VW00gcE*0*qXwMzHrmBFs-RNe;t|@&R^^^x zUh*7kv^}2ZCeJl}&+U8c;70x4AWZoBnk!?z?gq}AR9<@at$JUskNf`ou482AT$Qnv zf6|vP)}U^eqaL$RU6#(hI;cKzfE3()>*|?avl&m{ix*uTf9ZPpmYm$D5>v^d*qX6V z@Y`(gZOzz6v+L7eL$j~){*E++v-jI?97eD2@h<)vNc2QL{$tbq*U$d&FHG)#}*y*TU5yZ>M)j$~;{Louvu%LYJ7%!07Ygb*@( z)QpUi<|pJ3i^wD-sB9)1N5$kAby~03tai)oR#Jhmcs!03aJ3svL{(O;Z*AO5U)Jvu zsC~M}`~QG}f`bqP95OUEXKOosOb!bQkxP$#7LkvWl8{iHkp7ZYm6Mv6qC9h)pp>Z( zJ(QlCv7DNiuopp5p_QPdr#_^-xu|ZRtrTs9$;!*j&4&R4(9+S=(}+2WXl%tkzsB5B z-5Ips+_mB4A3=Hq7V6(WU?@krjQMV+qhmPt?Bg=9RnM0X zg9;r=Gz7(o6R)MQsH2h1TmoIC~O{6&dnhr`FsR^z5Jo8BL(Umk5gMjVH(^l@X13vnAciO+ zASrBSVgm>=HDXZJ`PJ5nFvck3j2)!l!i^D`fnkC)_UPk}Kn6LWjX1ghLyFo6spOJO zHtFOaM798pkvb;ABS`JMXwZ{fcIoAp%0$WjLJU+Q+1GnDab=uuV7BSzn{WytW;6gy z`9hT!sVH1mieb@VoPY)@XrOaGP-b*`wj>35?5t*;e*n?folmON_vfIPW~ym&V#45n zophRcnxb0G6w;ofh{dLDz-{X4tFQ(rW&~2=xm1#*TBd7PfT@}qGQGws?6AZ-6s4yR z*m)$PxW?40qVyy*mbAa>q-L?$W~(h8QBH8Ds7>YSYj8Bh(a~SJ2D|OL?6#W(k;pAY(o8ok+KDEbVPeTnS8esxTZDY$$hV&9mkBfb5w@S^HM&LE9gbZGcWTqY z9}-$(-Hg_U)?4ys4d%Tni%qnRZkm0U;2;(Fx#%7-tO?b&-f~l@0-X}f3-gu8f&HZ4 zt@Vlcmg5?sZ{UgZ%~Rt+b&Eod;Hqk0#SRm`iz$_6{Ff)?t(J*;tdECQKj*z3_qDXe zz8$67c^5kD{j`P4wZNMFXz;eVPPj>`K->B%$@@-wzYWLo@Zka%&zA6jlg`Ys5hy!3 z!vXc?l2(6z{?pqIqvOc@`2M~>|L2)^zBg_A0}du+qnOUP4_Yj7%s|R=7&gU59)^L5 z#(=^=f3Zq7_pl%J#sVHx3|*r!t| zGmNt&moHV58o)Udf30dJ|0451Um4RR+=Jl;lUTv*WO8HZ^hPa}fG{#$ahKD9*nV7r z9oOXtW3gLaDYoaGR3$`v_Q7ZMhTxytl(JH{D^LA~r#XgpPh$BgpFAZ9(A_CCpycr$ zPS*0q_WaQn5qoI$n%GZRgcNtcD~?J*xK2ae@&#L4AJr5Gw`vM*bZB$}O9!(co04Fs za5zg#!}QW3>C$dqd&o{3Sp=Z&bEqtJs!p*a)uE1nZtp}YFB|eR!BrFpzq4xJ!aAR1 z5~8bOJzoL-T;?*FCN-^ajjP9SrU5L<^{#kzSA~dnp+(LBYJd%_UP z_qy2St#Xr#T-R+kO$9v&VW8=I7vbm`Ki)XS;q zY3aGSxuqqgmF1Py*Qy(CG&D6f-MV?}PTQS(ckcCc_w?QGd+^`^^9gfobZla5VrFLM z`Sa(itE=nl>)*b8J2*J_`}gnvlKB4x<`?w;QC0sJ|5p>h#$uL*k=>d|t@SaJ5k@zh z0y_=jJsK$f3DaMapR9FIBD8dEmH9(Q&L-M?=}U3A`|8_q|D|W&#@8>U($mrz8FY*c zErwqiD~>5Dk`^s47MI3UR!T~i->9v-QQ1)6e6yvbwz0MC&fSiCGIh6$d+yz@>bv!* zspG+e;+h6Y*?d`yLg2Ktf}FhEGwJDR%(KP0<;B&NH*ep)THDy%+TOyvk`k9)oZ9%h zA|oy?H}zHS;PamR(ZtH@H$as*#Q{B-_LkVe=bVw>*%ix&KVR%yC$cgTLg=`TjFgZ0a80n+%17cPNzSLMRn^RivqUtfs|q?g(j zpS3Phe5c%fygodVsr)y;CBSc_n_K4a{6zU3|5wxJH7?BDe$X}b=(>3(evV2=g|Q2f z(c$Yv*d63;BDuDA5b}ykM3IKX3^GudoXWylG!X=OAnZn-s{?y-aAR>)JPib9<0al* ze0uuChp%fE%D07gp`9qC_yzp>XU%K;3i4LddZzusb4QWygGEh8KyV~_4+Mt0?YTk) zb*Kage;kqkVULJ@1ACx^I}PC$2q8KmSOk4AjZ4c31QCVy(IAtUT0DEV3G>Az97}NX z(lG-M=Ge*F8&utq%XoT0pt5j=E~@j(zoqMH$V^PuPBN2{t; z;ahE2#tg!3E9BddX3uJAGjLSaS}KeW2#KJKwJ|>5bK7D%vu&eGd zp~4Y+gK&l0xFINo_I{Y(xP;)xiHK%`{%oi)K~N|V9s0vEgf;-mOX&chs&>&}rR8wi z_`fI|ZR!V4Ein5F6|Mc$_~*=CIMNeoFN>wD5Fob?=nVjXuzQ_LaD>y3nPVQDB;a`^f;NhS;DJ;x@#w@=cEBlq83sl6 zfjqd@egYu$9)9gmY7ak^#YHs$!TS0EC`^KepOSc0qYJ>jQ)ufT#GWC*PC)=M0apK& z|F-7l)0oHX#~1tF2=O8bukN0jO&$f}z8-v^5C#tSvAQlp&&6?MCcOD1dIKujIKX6w zB^8RX@xpOn2n3H#9sW@a|J9#iPSb)QMF0@!;0!zAEy!;99*D31aT#!mfFC0(q%P{H zf?5snU?G|qr-3@~^%Nc=f&{n}(W!it8*gJE0E`L`Ync>>h&iP~44goR7ic-i3H-P( z`7E0O^WQ}}4XQ}h|M8GJm6L>CRILpRndXi;n?#6e3If|*!-omcm@<0IDBfu1(?TnY zG9_L=ztHQl)oo;3&<$z+35OA%-)gl~rlGIauRCgH)LWBe3LevhWX_+lg2Ck)Zc z&?zvXf_|BMy*(W3JFkn5BrluL*;jytm}g40@v-oHfogGo0|`cGILA`cr*O$p8Eu}yR&?Gvf9BEkT>0qPQW zmfWfglJ-WRpiEW=e@+B_SJ_M3cj^{%Um3^uL5T_y>bM4zKm$Qj8{pxATUj3EC!Jx^7hINJ;l~FwrUi$7%^e_*;vxmi}9JzZPx!i2+JSriqqE|RFo>41T<7c#0#^VzVEpQNzAlTx9~FVt*Y1HL z7Vnn7y6Sr}-0P(>)4$dHTHu$a^5k>-w=22Zqcg9)ob|g0_Ie!-%3E74E__=+~U; zkB_+TWm)~btKtIO^-`?@RHOSiW@d=nlIx~T>~ru!&A?@XA51hCZ~@~g*(;cjEi0aZ z>}vfM9yny6owV7g%$W6Utgf})w(^ygcK%vsHp3EJ4~hXgzfdCQ<%2GM<(ety?|M5# z_E_n>>km@Y1`YN9*RM41XV-X+ZT$6A`Sa^ptnSzBc@pTEhc5LZTX((iY{-q5?$aT} zvsYVl#4?3m)aF7dpICw)Se6%lFSw2EpFa55Picb#TzRot(wyV}7Jk*EO05gPKA0|> zI>(fge3y~Ysdj0>2WDFr9e(`k}fP5Knt~_NIB|ve<`b z5I{Gv|Hks@+}QmXwx`kBuTD*gCEw*LIcu2wr+4#E4^;9izpzkzK|Y8wT|<0G6p%~4suMhnZ`1~Ww&g-hjmxu0%X%2>KgU8m;F^CguFDGR*8!Y93#lo zeNhKoIY=xQFy)Glf=~%N1|N)V@QH_dzI zwr}+a5f-LTx+Cr$M1F{i^1%YXMlyXDfhD;VZd~~7lF$$f$>k!K4sA)UTE2o7PK`IkOGg4y-%*j8Z=tyy)CCZPgq@O zWDr?91d}G}(SBsBfd-o#5HOtC2y>cD*A4N{MFJPDCq-lQBBr%bK4bX&1v}z|1BfW5 z7X*;E2*tAcC|cBqxKrP*ni1i6Od{YrYQ&94oHkW5SrI_nNxUNk5f&MXgb zh%U)H+PIM1rLEj3espBctCjURow!7Dy`C0Hhe%41C6`E+vit$svW8sIy2_;vVX4lJ z0cl4^CqyTm`AhYdGMy24aRF?2o28_UuGZTG4KpZ^4>#g$0rV6J<=Vt0jPK{t>=lr$ zkHwj%$GD7MUu=oTq7J^!J@%+G(lJvHoR+{UzNBvD(6sIOdDGKPsmzzZjHL`I_j*v~ z&ynC5s+G3o3)e0s{kmAA8;V71xV;RGGuJ9DbEm`y1+xdiWXmxn`p&J6fh9Q6&8xcp zUMF=a1r|V+l?EF+A(NqbP#+W@O8JK#`{()T;w6o8QGI*eiV)fJr%zIp{41)q%ZrW@ zdf2lbHU;BcO&jH|mRozJ#aV~MYA@dlcB%Awv|HwKU%UUM=V6IvQ$zVJ_p6{^VV}E# z8M~UpN+~8y+SAth-PSeyWPL8B(~lLB{~11|=%$fAhzRtji_rb8k0TCGR{B(ybtt8>FT^GavNU;a;>(6hJ_q

!rEec%|p{R4>kf@g#fH7Ih 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 literal 0 HcmV?d00001 diff --git a/docs/screenshot_gui1.gif b/docs/screenshot_gui1.gif new file mode 100644 index 0000000000000000000000000000000000000000..233e18006cdf98ab5be7b476c452bbb6016bb396 GIT binary patch literal 42755 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 POW1@@7=HgZAOHY6oz3MC literal 0 HcmV?d00001 diff --git a/docs/screenshot_gui2.gif b/docs/screenshot_gui2.gif new file mode 100644 index 0000000000000000000000000000000000000000..aecdb35f230bfde4694246613966027d4cbb858b GIT binary patch literal 36887 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 S0&Ktv?7$LivvO-d0028?8)%0B literal 0 HcmV?d00001 diff --git a/docs/screenshot_gui3.gif b/docs/screenshot_gui3.gif new file mode 100644 index 0000000000000000000000000000000000000000..17950eed4946d73c0ee909a330fc8914afabcc34 GIT binary patch literal 37080 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 literal 0 HcmV?d00001 diff --git a/docs/screenshot_gui4.gif b/docs/screenshot_gui4.gif new file mode 100644 index 0000000000000000000000000000000000000000..31e60fa5413ee6806d8fea4f3346caaa656c4e6a GIT binary patch literal 40769 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 literal 0 HcmV?d00001 diff --git a/docs/screenshot_gui5.gif b/docs/screenshot_gui5.gif new file mode 100644 index 0000000000000000000000000000000000000000..1313db22b3010c7080e025c00d0b2e8991cb1eec GIT binary patch literal 35909 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 G1OPkx>vlu{ literal 0 HcmV?d00001 diff --git a/docs/screenshot_gui6.gif b/docs/screenshot_gui6.gif new file mode 100644 index 0000000000000000000000000000000000000000..c200b97a493b08f82c033368bcc0924365cd3829 GIT binary patch literal 37123 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 F06TY?PvZap literal 0 HcmV?d00001 diff --git a/docs/screenshot_gui8.gif b/docs/screenshot_gui8.gif new file mode 100644 index 0000000000000000000000000000000000000000..95a1a30b06e02d53b799786447d7b9c21e5635d3 GIT binary patch literal 31454 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 H05tvwAOB$U literal 0 HcmV?d00001 diff --git a/docs/screenshots.html b/docs/screenshots.html new file mode 100644 index 000000000..31c12966a --- /dev/null +++ b/docs/screenshots.html @@ -0,0 +1,67 @@ + + + + + + +ProGuard Screenshots + + + + +

Screenshots

+ + +
+ +GUI screenshot + + +ProGuard +Input/Output +Shrinking +Optimization +Obfuscation +Information +Process +ReTrace + + +The graphical user interface to ProGuard works like a wizard. It allows +you to browse through the presented tabs and fill them out. +

+You can click on the small tab buttons to see the full-size versions of the +tabs. + +

+ +Console screenshot + +Of course, real developers don't need all this point-and-click fluff. They +write short configuration files using their favorite text editors and invoke +ProGuard from the command-line. +

+You can click on the image to see the full-size version. + +

+ +
+
+
+Copyright © 2002-2013 +Eric Lafortune. +
+ + diff --git a/docs/screenshots_gui_small.gif b/docs/screenshots_gui_small.gif new file mode 100644 index 0000000000000000000000000000000000000000..bc32ccbb85f74add0b96bf0762f68de3ea5fff5c GIT binary patch literal 161022 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�r@KtS|7Mp;SZf-7Nf3v-r(<9`h3aUR4dv+kc1L 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

5OL_I}cA-^z}-< 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 + + + + + + +Sections + + + +

+ +

+

+More Android code protection: +

+ +Saikoa + +

+With support of + +

+ +DexGuard + +

+ +SourceForge + +

+ + + + + diff --git a/docs/sflogo.png b/docs/sflogo.png new file mode 100644 index 0000000000000000000000000000000000000000..142a6f99abdf58dcb17c5bfceba249335147b55b GIT binary patch literal 469 zcmV;`0V@89P)MgRZ*?(XhiYI3x>y^4~UpQfsG zeudxR7I>kR;N+`009w6L_t(Y$L-U}Zrm^wMA6&#aTF=F94E0e z{r_)fA-9KQ-5W!-Ai&K*p$KxhQ@C)KobU;s@MnXsU3(?e-f{*~E@}{JWM7hJS2t99 zt6hynb%A6sP3m!2E?jmi5!;xwUT$y@hC$6aLo-3MRI*uF{4q);w#5J-X`Ln$z+xOz z#?Jbrsjw(5UVZ0bf<*K6x) zx|U!X?;bA!v+D?y-Tu9(>$B?5KfjWVIHoA?rZ_*y37_yMg71{SQ}zkFi^+QO00000 LNkvXXu0mjf5NYAx literal 0 HcmV?d00001 diff --git a/docs/steel.gif b/docs/steel.gif new file mode 100644 index 0000000000000000000000000000000000000000..307b57abe7288209f1a4124d0f553d6fd8821f5b GIT binary patch literal 2759 zcmb`?i8~XF1HkdQCt|L($*oK|3h^eA+{au|2y^5XA!fN^hB>ypBO>HxRPG~3j$AP_ zw%KfsAxBcF-Z%2f`+MI1;rIOmKF{;Dw6?hVhc60%0@MNk>?O`3YlXpFqA#$hH0Ip= z!YqY0L!O@|QQl9H*-I;ntR)7MMPFc2X$y1nv{}mh44E=bBEO#^vDmB3#pMMCn?_%p zr!pvWbn+~fG&46fJH3c!6ZSrrcYcA+&6Ln=TD-P5&K?V#9DSe>-wD{g*9`|s+`Wq3&l~Y~n^G_}Z4ohy z<~`eB_$fuGsa;qlYiCkiItzKl|X zhhEH8Tm4pan~J$GSnv49xw3f9)_a0kmI$3WZ%^?h?}7MGUnGqi6@n0I(3}|(oVIC80?@8B!v!ZzYKuEjA|IDJgTLJNTcXpJ_T#p;n?!7{9+o>lkqpD-HxwCyTHIb80#kc;K zJ@5k-YM(o3dd5x~@3D&RK(o6+8<+?qtA;|)g)2btNz71XQMrTCRT%I1La%0Dn$+Q^ z>zy=ZgkM{_Z6gsNus7Vp`$A8~+-6eH`}$s;Od zN(E<6FkzBctA$6J*i(AtIcjNv9*tsU4Ex99oiM$GZ{)4@nK{Hi@b=HDeV-QJBxNEy zS8h_R`Q;Lrkj{tQyIr*0N5uR<2k(7#jO8Bl^%P#RLYxw6<+~Dux42=9qZ~(Aj;>M1@Zgg$((=NkG z>#ubBafJC7K6<)FX6vYgho{yHz#i2FATIjdx+YRHoA#6dd!4VJ5tGBkDvI`lX(jTB z=n~*FOxK#GN0w+;091uLO5DHf%gxBfyqFdG^Fe?=$~IJhly|ElPBAMzx~iyE1|X{h z;Ye93V5%ggQk0}yv9))HHplt?@Vc4drKAI}^tol258{aAhEDXoR9&r8u=SKt~Urca~_6dV?wf#{X5xgL6QIv~o^ zr`~X5lOWs@$(guUm94_9KIk12%6qbvnil`k2&b!}vVc?t4nE`)&(O2Cx?pz+osZ7F z)-8S$lzz?>2LGt0dC*GGWuOT~lU6I!{x4 z439dOqC4tKFuItB@)U{*@To&ch`-hI611UxhbYoA?OM4D#6$cY$u6qU-fM+wfAv)P z=5Xxm99Af#7(FC{LJ7zrfoy9=gG#mp9f6(c`dh67A(xG>Wxh^IXl*Zk8rYP zQSMORopk>lB_ZFAU|)MROjj zeNa_TN@&|jH(I66=9E=)JYyQchx$6O;^AK$X|uX7B~~AYeQM$tirpLDg4dpU4S935 z{-@)Zx;S0nc7yg=Mnt9>Vy)9dDg}kgv%{*}%Jp04bAY>Gf%a%TPae;Ogn&SYD$vH+vIESR z=Zel)`)NIRjj+sQTVZ=?ie|#EX&?I`)9SX;xQ~1Wl18vWb)FSxqdT&~>&>O|P{YO4 z2GfxNkbrIf*|j}no1Mo6uE4Oqt&8I|gmrU*m}1b;wO(#Z7nhXSpPX@Bd|@jf+n@y5 zeZn0JGzCGT00$F;@L_?ZP2(>x!@>!!+&d<{4-xOAyMx3?xm8M$VekygDV)rrs?0qH z-s~T8aNqlRi7ch^apjs9wRo4X z59kMw?l+j$PkP&2O9*U`FOU83$TCvYMD_>2NpgY4gGXyGs;1;bnlQ-q+rqrtTZGnU z_~=~SjR4emwpC-E!7*DN5oUp#)8LCxxtJKX_u51Kd#)zv_eaGKW2Iw7SmNK#11poG zD0KxqZ^r)9!J`E=?PhpS9%^mIJqtmdenk!*Oq8Y;+8Q1o{^0vZYXUXVcQF>nF8PY#tw|7+pgNMH4E6E0^pM@A?4`1(ZX<1~iYx^ov>wAudiyQwwt2k*d$^$M7bn?v-y z_9C-drxfIx+tL-P1h3#$q1Y9Z8~GCfsre$DqvKQmMqMWvHRsetf#a`(njtHRYUD>6 z7`W{1>|3$|KMXH1@XW4zTmfwp`Zn>CS#|N6>r<|bGv`PBmIKR^m+DQ|G{WRQraH42 nW^WH*TK&>Z!*TsPNn + + + + + +ProGuard Testimonials + + + + +

Testimonials

+ +And now for some shameless self-glorification and name-dropping... +

+ProGuard is probably the most popular java shrinker, optimizer, and +obfuscator world-wide. It is being used by developers at companies and +organizations like IBM, HP, Siemens, Nokia, Google, Intel, and NATO. It is the +default tool in many development environments like Oracle's Wireless Toolkit, +Netbeans, EclipseME, Google's Android SDK, and more. Although the quotes below +probably don't represent official views of any kind, encouragements like these +do keep me happy. +

+ +

+ + + + +

+ProGuard is the ultimate java obfuscator! +

+

P.S, IBM

+

+ +Also: +

+ + + + +

+ProGuard is pure quality - powerful and trouble-free. +

+

M.B., Statestep

+

+ +And: +

+ + + + +

+It is the simplest and most robust obfuscator we have ever used. +

+

I.I., Hewlett-Packard

+

+ +And indeed: +

+ + + + +

+ProGuard rules. Much easier to use than the commercial alternatives. +

+

B.G., Quiotix Corp.

+

+ +Straight from ProGuard's open discussion forum: +

+

+ + + + +

+After searching for, trying to trial, and futzing with numerous other +obfuscators and shrinkers, ProGuard stands out as the simplest, most robust, +and accurate shrinker of them all. +

+

D.J., Joot

+

+ +From the article "Obfuscating MIDlet Suites with ProGuard" at developers.sun.com: +

+

+ + + + +

+Its friendly license, attractive price tag, compelling performance, and +powerful configuration options make it an excellent addition to your MIDlet +development toolbox. +

+

J.K., Sun

+

+ +And, of course, the price is stunning: +

+

+ + + + +

+You could've been rich. +

+

My mother

+ +
+ +
+Copyright © 2002-2013 +Eric Lafortune. +
+ + + diff --git a/docs/title.gif b/docs/title.gif new file mode 100644 index 0000000000000000000000000000000000000000..5e6ca26147abbbff8a94b3991fb6368f42603bf1 GIT binary patch literal 2613 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 literal 0 HcmV?d00001 diff --git a/docs/title.html b/docs/title.html new file mode 100644 index 000000000..7ec9581e3 --- /dev/null +++ b/docs/title.html @@ -0,0 +1,17 @@ + + + + + + +ProGuard + + + +
+

ProGuard

+
Version 4.9
+
+ + + diff --git a/examples/android.pro b/examples/android.pro new file mode 100644 index 000000000..16e648f12 --- /dev/null +++ b/examples/android.pro @@ -0,0 +1,168 @@ +# +# This ProGuard configuration file illustrates how to process Android +# applications. +# Usage: +# java -jar proguard.jar @android.pro +# +# If you're using the Android SDK, the Ant release build and Eclipse export +# already take care of the proper settings. You only need to enable ProGuard +# by commenting in the corresponding line in project.properties. You can still +# add project-specific configuration in proguard-project.txt. +# +# This configuration file is for custom, stand-alone builds. + +# Specify the input jars, output jars, and library jars. +# Note that ProGuard works with Java bytecode (.class), +# before the dex compiler converts it into Dalvik code (.dex). + +-injars bin/classes +-injars libs +-outjars bin/classes-processed.jar + +-libraryjars /usr/local/android-sdk/platforms/android-9/android.jar +#-libraryjars /usr/local/android-sdk/add-ons/google_apis-7_r01/libs/maps.jar +# ... + +# Save the obfuscation mapping to a file, so you can de-obfuscate any stack +# traces later on. + +-printmapping bin/classes-processed.map + +# You can print out the seeds that are matching the keep options below. + +#-printseeds bin/classes-processed.seeds + +# Preverification is irrelevant for the dex compiler and the Dalvik VM. + +-dontpreverify + +# Reduce the size of the output some more. + +-repackageclasses '' +-allowaccessmodification + +# Switch off some optimizations that trip older versions of the Dalvik VM. + +-optimizations !code/simplification/arithmetic + +# Keep a fixed source file attribute and all line number tables to get line +# numbers in the stack traces. +# You can comment this out if you're not interested in stack traces. + +-renamesourcefileattribute SourceFile +-keepattributes SourceFile,LineNumberTable + +# RemoteViews might need annotations. + +-keepattributes *Annotation* + +# Preserve all fundamental application classes. + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider + +# Preserve all View implementations, their special context constructors, and +# their setters. + +-keep public class * extends android.view.View { + public (android.content.Context); + public (android.content.Context, android.util.AttributeSet); + public (android.content.Context, android.util.AttributeSet, int); + public void set*(...); +} + +# Preserve all classes that have special context constructors, and the +# constructors themselves. + +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet); +} + +# Preserve all classes that have special context constructors, and the +# constructors themselves. + +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +# Preserve all possible onClick handlers. + +-keepclassmembers class * extends android.content.Context { + public void *(android.view.View); + public void *(android.view.MenuItem); +} + +# Preserve the special fields of all Parcelable implementations. + +-keepclassmembers class * implements android.os.Parcelable { + static android.os.Parcelable$Creator CREATOR; +} + +# Preserve static fields of inner classes of R classes that might be accessed +# through introspection. + +-keepclassmembers class **.R$* { + public static ; +} + +# Preserve the required interface from the License Verification Library +# (but don't nag the developer if the library is not used at all). + +-keep public interface com.android.vending.licensing.ILicensingService + +-dontnote com.android.vending.licensing.ILicensingService + +# The Android Compatibility library references some classes that may not be +# present in all versions of the API, but we know that's ok. + +-dontwarn android.support.** + +# Preserve all native method names and the names of their classes. + +-keepclasseswithmembernames class * { + native ; +} + +# Preserve the special static methods that are required in all enumeration +# classes. + +-keepclassmembers class * extends java.lang.Enum { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Explicitly preserve all serialization members. The Serializable interface +# is only a marker interface, so it wouldn't save them. +# You can comment this out if your application doesn't use serialization. +# If your code contains serializable classes that have to be backward +# compatible, please refer to the manual. + +-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 + +# If you wish, you can let the optimization step remove Android logging calls. + +#-assumenosideeffects class android.util.Log { +# public static boolean isLoggable(java.lang.String, int); +# public static int v(...); +# public static int i(...); +# public static int w(...); +# public static int d(...); +# public static int e(...); +#} diff --git a/examples/annotations/examples.pro b/examples/annotations/examples.pro new file mode 100644 index 000000000..3a47183e1 --- /dev/null +++ b/examples/annotations/examples.pro @@ -0,0 +1,60 @@ +# +# This ProGuard configuration file illustrates how to use annotations for +# specifying which classes and class members should be kept. +# Usage: +# java -jar proguard.jar @examples.pro +# + +# Specify the input, output, and library jars. +# This is assuming the code has been compiled in the examples directory. + +-injars examples(*.class) +-outjars out + +-libraryjars /lib/rt.jar + +# Some important configuration is based on the annotations in the code. +# We have to specify what the annotations mean to ProGuard. + +-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 themselves. + +-keepattributes *Annotation* + +# Preserve all native method names and the names of their classes. + +-keepclasseswithmembernames class * { + native ; +} + +# Preserve the special static methods that are required in all enumeration +# classes. + +-keepclassmembers class * extends java.lang.Enum { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Explicitly preserve all serialization members. The Serializable interface +# is only a marker interface, so it wouldn't save them. +# You can comment this out if your application doesn't use serialization. +# If your code contains serializable classes that have to be backward +# compatible, please refer to the manual. + +-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(); +} diff --git a/examples/annotations/examples/Applet.java b/examples/annotations/examples/Applet.java new file mode 100644 index 000000000..8a5874b44 --- /dev/null +++ b/examples/annotations/examples/Applet.java @@ -0,0 +1,22 @@ +import proguard.annotation.*; + +/** + * This applet illustrates the use of annotations for configuring ProGuard. + * + * You can compile it with: + * javac -classpath ../lib/annotations.jar Applet.java + * You can then process it with: + * java -jar ../../../lib/proguard.jar @ ../examples.pro + * + * The annotation will preserve the class and its essential methods. + */ +@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 000000000..f8d506063 --- /dev/null +++ b/examples/annotations/examples/Application.java @@ -0,0 +1,20 @@ +import proguard.annotation.KeepApplication; + +/** + * This application illustrates the use of annotations for configuring ProGuard. + * + * You can compile it with: + * javac -classpath ../lib/annotations.jar Application.java + * You can then process it with: + * java -jar ../../../lib/proguard.jar @ ../examples.pro + * + * The annotation will preserve the class and its main method. + */ +@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 000000000..0544bf35d --- /dev/null +++ b/examples/annotations/examples/Bean.java @@ -0,0 +1,56 @@ +import proguard.annotation.*; + +/** + * This bean illustrates the use of annotations for configuring ProGuard. + * + * You can compile it with: + * javac -classpath ../lib/annotations.jar Bean.java + * You can then process it with: + * java -jar ../../../lib/proguard.jar @ ../examples.pro + * + * The annotations will preserve the class and its public getters and setters. + */ +@Keep +@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 000000000..2c72f7b18 --- /dev/null +++ b/examples/annotations/examples/NativeCallBack.java @@ -0,0 +1,44 @@ +import proguard.annotation.*; + +/** + * This application illustrates the use of annotations for configuring ProGuard. + * + * You can compile it with: + * javac -classpath ../lib/annotations.jar NativeCallBack.java + * You can then process it with: + * java -jar ../../../lib/proguard.jar @ ../examples.pro + * + * The annotation will preserve the class and its main method. + */ +@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 0000000000000000000000000000000000000000..60d0d4f0caa0b2260d03cf2a6633f4bc58d793c2 GIT binary patch literal 6126 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/ant/applications1.xml b/examples/ant/applications1.xml new file mode 100644 index 000000000..6df5789eb --- /dev/null +++ b/examples/ant/applications1.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/examples/ant/applications2.xml b/examples/ant/applications2.xml new file mode 100644 index 000000000..681a8940a --- /dev/null +++ b/examples/ant/applications2.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + -injars in.jar + -outjars out.jar + + -libraryjars ${java.home}/lib/rt.jar + + + + + + + + -printmapping out.map + -renamesourcefileattribute SourceFile + -keepattributes SourceFile,LineNumberTable + + + + -keepattributes *Annotation* + + + + -keepclasseswithmembers public class * { + public static void main(java.lang.String[]); + } + + + + -keepclasseswithmembernames class * { + native <methods>; + } + + + + -keepclassmembers class * extends java.lang.Enum { + public static **[] values(); + public static ** valueOf(java.lang.String); + } + + + + -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(); + } + + + + + + + diff --git a/examples/ant/applications3.xml b/examples/ant/applications3.xml new file mode 100644 index 000000000..e42eb39a9 --- /dev/null +++ b/examples/ant/applications3.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/ant/library.xml b/examples/ant/library.xml new file mode 100644 index 000000000..d87bd16cb --- /dev/null +++ b/examples/ant/library.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/ant/midlets.xml b/examples/ant/midlets.xml new file mode 100644 index 000000000..223d0daa2 --- /dev/null +++ b/examples/ant/midlets.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/ant/proguard.xml b/examples/ant/proguard.xml new file mode 100644 index 000000000..b5c92a9e2 --- /dev/null +++ b/examples/ant/proguard.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/ant/servlets.xml b/examples/ant/servlets.xml new file mode 100644 index 000000000..51bcaad63 --- /dev/null +++ b/examples/ant/servlets.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/applets.pro b/examples/applets.pro new file mode 100644 index 000000000..c5affc130 --- /dev/null +++ b/examples/applets.pro @@ -0,0 +1,69 @@ +# +# This ProGuard configuration file illustrates how to process applets. +# Usage: +# java -jar proguard.jar @applets.pro +# + +# Specify the input jars, output jars, and library jars. + +-injars in.jar +-outjars out.jar + +-libraryjars /lib/rt.jar + +# Save the obfuscation mapping to a file, so you can de-obfuscate any stack +# traces later on. Keep a fixed source file attribute and all line number +# tables to get line numbers in the stack traces. +# You can comment this out if you're not interested in stack traces. + +-printmapping out.map +-renamesourcefileattribute SourceFile +-keepattributes SourceFile,LineNumberTable + +# Preserve all annotations. + +-keepattributes *Annotation* + +# You can print out the seeds that are matching the keep options below. + +#-printseeds out.seeds + +# Preserve all public applets. + +-keep public class * extends java.applet.Applet + +# Preserve all native method names and the names of their classes. + +-keepclasseswithmembernames class * { + native ; +} + +# Preserve the special static methods that are required in all enumeration +# classes. + +-keepclassmembers class * extends java.lang.Enum { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Explicitly preserve all serialization members. The Serializable interface +# is only a marker interface, so it wouldn't save them. +# You can comment this out if your library doesn't use serialization. +# If your code contains serializable classes that have to be backward +# compatible, please refer to the manual. + +-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/applications.pro b/examples/applications.pro new file mode 100644 index 000000000..f71808863 --- /dev/null +++ b/examples/applications.pro @@ -0,0 +1,75 @@ +# +# This ProGuard configuration file illustrates how to process applications. +# Usage: +# java -jar proguard.jar @applications.pro +# + +# Specify the input jars, output jars, and library jars. + +-injars in.jar +-outjars out.jar + +-libraryjars /lib/rt.jar +#-libraryjars junit.jar +#-libraryjars servlet.jar +#-libraryjars jai_core.jar +#... + +# Save the obfuscation mapping to a file, so you can de-obfuscate any stack +# traces later on. Keep a fixed source file attribute and all line number +# tables to get line numbers in the stack traces. +# You can comment this out if you're not interested in stack traces. + +-printmapping out.map +-renamesourcefileattribute SourceFile +-keepattributes SourceFile,LineNumberTable + +# Preserve all annotations. + +-keepattributes *Annotation* + +# You can print out the seeds that are matching the keep options below. + +#-printseeds out.seeds + +# Preserve all public applications. + +-keepclasseswithmembers public class * { + public static void main(java.lang.String[]); +} + +# Preserve all native method names and the names of their classes. + +-keepclasseswithmembernames class * { + native ; +} + +# Preserve the special static methods that are required in all enumeration +# classes. + +-keepclassmembers class * extends java.lang.Enum { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Explicitly preserve all serialization members. The Serializable interface +# is only a marker interface, so it wouldn't save them. +# You can comment this out if your application doesn't use serialization. +# If your code contains serializable classes that have to be backward +# compatible, please refer to the manual. + +-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/dictionaries/compact.txt b/examples/dictionaries/compact.txt new file mode 100644 index 000000000..5636a3eed --- /dev/null +++ b/examples/dictionaries/compact.txt @@ -0,0 +1,18 @@ +# +# This obfuscation dictionary contains strings that are already present +# in many class files. Since these strings can be shared, the resulting +# obfuscated class files will generally be a little bit more compact. +# Usage: +# java -jar proguard.jar ..... -obfuscationdictionary compact.txt +# + +Code +V +I +Z +B +C +S +F +D +L diff --git a/examples/dictionaries/keywords.txt b/examples/dictionaries/keywords.txt new file mode 100644 index 000000000..76f5a7b0a --- /dev/null +++ b/examples/dictionaries/keywords.txt @@ -0,0 +1,58 @@ +# +# This obfuscation dictionary contains reserved Java keywords. They can't +# be used in Java source files, but they can be used in compiled class files. +# Note that this hardly improves the obfuscation. Decent decompilers can +# automatically replace reserved keywords, and the effect can fairly simply be +# undone by obfuscating again with simpler names. +# Usage: +# java -jar proguard.jar ..... -obfuscationdictionary keywords.txt +# + +do +if +for +int +new +try +byte +case +char +else +goto +long +this +void +break +catch +class +const +final +float +short +super +throw +while +double +import +native +public +return +static +switch +throws +boolean +default +extends +finally +package +private +abstract +continue +strictfp +volatile +interface +protected +transient +implements +instanceof +synchronized diff --git a/examples/dictionaries/shakespeare.txt b/examples/dictionaries/shakespeare.txt new file mode 100644 index 000000000..28b1cd8d5 --- /dev/null +++ b/examples/dictionaries/shakespeare.txt @@ -0,0 +1,23 @@ +# +# This obfuscation dictionary contains quotes from plays by Shakespeare. +# It illustrates that any text can be used, for whatever flippant reasons +# one may have. +# Usage: +# java -jar proguard.jar ..... -obfuscationdictionary shakespeare.txt +# + + +"This thing of darkness I acknowledge mine." + + --From The Tempest (V, i, 275-276) + + +"Though this be madness, yet there is method in 't." + + --From Hamlet (II, ii, 206) + + +"What's in a name? That which we call a rose + By any other word would smell as sweet." + + --From Romeo and Juliet (II, ii, 1-2) diff --git a/examples/dictionaries/windows.txt b/examples/dictionaries/windows.txt new file mode 100644 index 000000000..fd65dc977 --- /dev/null +++ b/examples/dictionaries/windows.txt @@ -0,0 +1,209 @@ +# +# This obfuscation dictionary contains names that are not allowed as file names +# in Windows, not even with extensions like .class or .java. They can however +# be used without problems in jar archives, which just begs to apply them as +# obfuscated class names. Trying to unpack the obfuscated archives in Windows +# will probably generate some sparks. +# Usage: +# java -jar proguard.jar ..... -classobfuscationdictionary windows.txt +# -packageobfuscationdictionary windows.txt +# + +aux +Aux +aUx +AUx +auX +AuX +aUX +AUX +AUX +con +Con +cOn +COn +coN +CoN +cON +CON +CON +nul +Nul +nUl +NUl +nuL +NuL +nUL +NUL +NUL +prn +Prn +pRn +PRn +prN +PrN +pRN +PRN +PRN +com1 +Com1 +cOm1 +COm1 +coM1 +CoM1 +cOM1 +COM1 +COM1 +com2 +Com2 +cOm2 +COm2 +coM2 +CoM2 +cOM2 +COM2 +COM2 +com3 +Com3 +cOm3 +COm3 +coM3 +CoM3 +cOM3 +COM3 +COM3 +com4 +Com4 +cOm4 +COm4 +coM4 +CoM4 +cOM4 +COM4 +COM4 +com5 +Com5 +cOm5 +COm5 +coM5 +CoM5 +cOM5 +COM5 +COM5 +com6 +Com6 +cOm6 +COm6 +coM6 +CoM6 +cOM6 +COM6 +COM6 +com7 +Com7 +cOm7 +COm7 +coM7 +CoM7 +cOM7 +COM7 +COM7 +com8 +Com8 +cOm8 +COm8 +coM8 +CoM8 +cOM8 +COM8 +COM8 +com9 +Com9 +cOm9 +COm9 +coM9 +CoM9 +cOM9 +COM9 +COM9 +lpt1 +Lpt1 +lPt1 +LPt1 +lpT1 +LpT1 +lPT1 +LPT1 +LPT1 +lpt2 +Lpt2 +lPt2 +LPt2 +lpT2 +LpT2 +lPT2 +LPT2 +LPT2 +lpt3 +Lpt3 +lPt3 +LPt3 +lpT3 +LpT3 +lPT3 +LPT3 +LPT3 +lpt4 +Lpt4 +lPt4 +LPt4 +lpT4 +LpT4 +lPT4 +LPT4 +LPT4 +lpt5 +Lpt5 +lPt5 +LPt5 +lpT5 +LpT5 +lPT5 +LPT5 +LPT5 +lpt6 +Lpt6 +lPt6 +LPt6 +lpT6 +LpT6 +lPT6 +LPT6 +LPT6 +lpt7 +Lpt7 +lPt7 +LPt7 +lpT7 +LpT7 +lPT7 +LPT7 +LPT7 +lpt8 +Lpt8 +lPt8 +LPt8 +lpT8 +LpT8 +lPT8 +LPT8 +LPT8 +lpt9 +Lpt9 +lPt9 +LPt9 +lpT9 +LpT9 +lPT9 +LPT9 +LPT9 diff --git a/examples/gradle/android.gradle b/examples/gradle/android.gradle new file mode 100644 index 000000000..6a377e6f1 --- /dev/null +++ b/examples/gradle/android.gradle @@ -0,0 +1,189 @@ +// +// This Gradle build file illustrates how to process Android +// applications. +// Usage: +// gradle -b android.gradle proguard +// +// If you're using the Android SDK, the Ant release build and Eclipse export +// already take care of the proper settings. You only need to enable ProGuard +// by commenting in the corresponding line in project.properties. You can still +// add project-specific configuration in proguard-project.txt. +// +// This configuration file is for custom, stand-alone builds. + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + // Note that ProGuard works with Java bytecode (.class), + // before the dex compiler converts it into Dalvik code (.dex). + + injars 'bin/classes' + injars 'libs' + outjars 'bin/classes-processed.jar' + + libraryjars '/usr/local/android-sdk/platforms/android-9/android.jar' + //libraryjars '/usr/local/android-sdk/add-ons/google_apis-7_r01/libs/maps.jar' + // ... + + // Save the obfuscation mapping to a file, so you can de-obfuscate any stack + // traces later on. + + printmapping 'bin/classes-processed.map' + + // You can print out the seeds that are matching the keep options below. + + //printseeds 'bin/classes-processed.seeds' + + // Preverification is irrelevant for the dex compiler and the Dalvik VM. + + dontpreverify + + // Reduce the size of the output some more. + + repackageclasses '' + allowaccessmodification + + // Switch off some optimizations that trip older versions of the Dalvik VM. + + optimizations '!code/simplification/arithmetic' + + // Keep a fixed source file attribute and all line number tables to get line + // numbers in the stack traces. + // You can comment this out if you're not interested in stack traces. + + renamesourcefileattribute 'SourceFile' + keepattributes 'SourceFile,LineNumberTable' + + // RemoteViews might need annotations. + + keepattributes '*Annotation*' + + // Preserve all fundamental application classes. + + keep 'public class * extends android.app.Activity' + keep 'public class * extends android.app.Application' + keep 'public class * extends android.app.Service' + keep 'public class * extends android.content.BroadcastReceiver' + keep 'public class * extends android.content.ContentProvider' + + // Preserve all View implementations, their special context constructors, and + // their setters. + + keep 'public class * extends android.view.View { \ + public (android.content.Context); \ + public (android.content.Context, android.util.AttributeSet); \ + public (android.content.Context, android.util.AttributeSet, int); \ + public void set*(...); \ + }' + + // Preserve all classes that have special context constructors, and the + // constructors themselves. + + keepclasseswithmembers 'class * { \ + public (android.content.Context, android.util.AttributeSet); \ + }' + + // Preserve all classes that have special context constructors, and the + // constructors themselves. + + keepclasseswithmembers 'class * { \ + public (android.content.Context, android.util.AttributeSet, int); \ + }' + + // Preserve all possible onClick handlers. + + keepclassmembers 'class * extends android.content.Context { \ + public void *(android.view.View); \ + public void *(android.view.MenuItem); \ + }' + + // Preserve the special fields of all Parcelable implementations. + + keepclassmembers 'class * implements android.os.Parcelable { \ + static android.os.Parcelable$Creator CREATOR; \ + }' + + // Preserve static fields of inner classes of R classes that might be accessed + // through introspection. + + keepclassmembers 'class **.R$* { \ + public static ; \ + }' + + // Preserve the required interface from the License Verification Library + // (but don't nag the developer if the library is not used at all). + + keep 'public interface com.android.vending.licensing.ILicensingService' + + dontnote 'com.android.vending.licensing.ILicensingService' + + // The Android Compatibility library references some classes that may not be + // present in all versions of the API, but we know that's ok. + + dontwarn 'android.support.**' + + // Preserve all native method names and the names of their classes. + + keepclasseswithmembernames 'class * { \ + native ; \ + }' + + // Preserve the special static methods that are required in all enumeration + // classes. + + keepclassmembers 'class * extends java.lang.Enum { \ + public static **[] values(); \ + public static ** valueOf(java.lang.String); \ + }' + + // Explicitly preserve all serialization members. The Serializable interface + // is only a marker interface, so it wouldn't save them. + // You can comment this out if your application doesn't use serialization. + // If your code contains serializable classes that have to be backward + // compatible, please refer to the manual. + + 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' + + // If you wish, you can let the optimization step remove Android logging + // calls. + //assumenosideeffects class android.util.Log { + // public static boolean isLoggable(java.lang.String, int); + // public static int v(...); + // public static int i(...); + // public static int w(...); + // public static int d(...); + // public static int e(...); + //} +} diff --git a/examples/gradle/applets.gradle b/examples/gradle/applets.gradle new file mode 100644 index 000000000..2c48b6e0f --- /dev/null +++ b/examples/gradle/applets.gradle @@ -0,0 +1,90 @@ +// +// This Gradle build file illustrates how to process applets. +// Usage: +// gradle -b applets.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + + injars 'in.jar' + outjars 'out.jar' + + libraryjars '/lib/rt.jar' + + // Save the obfuscation mapping to a file, so you can de-obfuscate any stack + // traces later on. Keep a fixed source file attribute and all line number + // tables to get line numbers in the stack traces. + // You can comment this out if you're not interested in stack traces. + + printmapping 'out.map' + renamesourcefileattribute 'SourceFile' + keepattributes 'SourceFile,LineNumberTable' + + // Preserve all annotations. + + keepattributes '*Annotation*' + + // You can print out the seeds that are matching the keep options below. + + //printseeds 'out.seeds' + + // Preserve all public applets. + + keep 'public class * extends java.applet.Applet' + + // Preserve all native method names and the names of their classes. + + keepclasseswithmembernames 'class * { \ + native ; \ + }' + + // Preserve the special static methods that are required in all enumeration + // classes. + + keepclassmembers 'class * extends java.lang.Enum { \ + public static **[] values(); \ + public static ** valueOf(java.lang.String); \ + }' + + // Explicitly preserve all serialization members. The Serializable interface + // is only a marker interface, so it wouldn't save them. + // You can comment this out if your library doesn't use serialization. + // If your code contains serializable classes that have to be backward + // compatible, please refer to the manual. + + 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/gradle/applications.gradle b/examples/gradle/applications.gradle new file mode 100644 index 000000000..1cffb2b9b --- /dev/null +++ b/examples/gradle/applications.gradle @@ -0,0 +1,96 @@ +// +// This Gradle build file illustrates how to process applications. +// Usage: +// gradle -b applications.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + + injars 'in.jar' + outjars 'out.jar' + + libraryjars '/lib/rt.jar' + //libraryjars 'junit.jar' + //libraryjars 'servlet.jar' + //libraryjars 'jai_core.jar' + //... + + // Save the obfuscation mapping to a file, so you can de-obfuscate any stack + // traces later on. Keep a fixed source file attribute and all line number + // tables to get line numbers in the stack traces. + // You can comment this out if you're not interested in stack traces. + + printmapping 'out.map' + renamesourcefileattribute 'SourceFile' + keepattributes 'SourceFile,LineNumberTable' + + // Preserve all annotations. + + keepattributes '*Annotation*' + + // You can print out the seeds that are matching the keep options below. + + //printseeds 'out.seeds' + + // Preserve all public applications. + + keepclasseswithmembers 'public class * { \ + public static void main(java.lang.String[]); \ + }' + + // Preserve all native method names and the names of their classes. + + keepclasseswithmembernames 'class * { \ + native ; \ + }' + + // Preserve the special static methods that are required in all enumeration + // classes. + + keepclassmembers 'class * extends java.lang.Enum { \ + public static **[] values(); \ + public static ** valueOf(java.lang.String); \ + }' + + // Explicitly preserve all serialization members. The Serializable interface + // is only a marker interface, so it wouldn't save them. + // You can comment this out if your application doesn't use serialization. + // If your code contains serializable classes that have to be backward + // compatible, please refer to the manual. + + 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/gradle/library.gradle b/examples/gradle/library.gradle new file mode 100644 index 000000000..92f5c68e4 --- /dev/null +++ b/examples/gradle/library.gradle @@ -0,0 +1,99 @@ +// +// This Gradle build file illustrates how to process a program +// library, such that it remains usable as a library. +// Usage: +// gradle -b library.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + // In this case, the input jar is the program library that we want to process. + + injars 'in.jar' + outjars 'out.jar' + + libraryjars '/lib/rt.jar' + + // Save the obfuscation mapping to a file, so we can de-obfuscate any stack + // traces later on. Keep a fixed source file attribute and all line number + // tables to get line numbers in the stack traces. + // You can comment this out if you're not interested in stack traces. + + printmapping 'out.map' + keepparameternames + renamesourcefileattribute 'SourceFile' + keepattributes 'Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod' + + // Preserve all annotations. + + keepattributes '*Annotation*' + + // Preserve all public classes, and their public and protected fields and + // methods. + + keep 'public class * { \ + public protected *; \ + }' + + // Preserve all .class method names. + + keepclassmembernames 'class * { \ + java.lang.Class class$(java.lang.String); \ + java.lang.Class class$(java.lang.String, boolean); \ + }' + + // Preserve all native method names and the names of their classes. + + keepclasseswithmembernames 'class * { \ + native ; \ + }' + + // Preserve the special static methods that are required in all enumeration + // classes. + + keepclassmembers 'class * extends java.lang.Enum { \ + public static **[] values(); \ + public static ** valueOf(java.lang.String); \ + }' + + // Explicitly preserve all serialization members. The Serializable interface + // is only a marker interface, so it wouldn't save them. + // You can comment this out if your library doesn't use serialization. + // If your code contains serializable classes that have to be backward + // compatible, please refer to the manual. + + 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 library 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/gradle/midlets.gradle b/examples/gradle/midlets.gradle new file mode 100644 index 000000000..dffb2c3f2 --- /dev/null +++ b/examples/gradle/midlets.gradle @@ -0,0 +1,88 @@ +// +// This Gradle build file illustrates how to process J2ME midlets. +// Usage: +// gradle -b midlets.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + + injars 'in.jar' + outjars 'out.jar' + + libraryjars '/usr/local/java/wtk2.5.2/lib/midpapi20.jar' + libraryjars '/usr/local/java/wtk2.5.2/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. + + overloadaggressively + + // Put all obfuscated classes into the nameless root package. + + repackageclasses '' + + // Allow classes and class members to be made public. + + allowaccessmodification + + // On Windows, you can't use mixed case class names, + // should you still want to use the preverify tool. + // + // dontusemixedcaseclassnames + + // Save the obfuscation mapping to a file, so you can de-obfuscate any stack + // traces later on. + + printmapping 'out.map' + + // You can keep a fixed source file attribute and all line number tables to + // get stack traces with line numbers. + + //renamesourcefileattribute 'SourceFile' + //keepattributes 'SourceFile,LineNumberTable' + + // You can print out the seeds that are matching the keep options below. + + //printseeds 'out.seeds' + + // Preserve all public midlets. + + keep 'public class * extends javax.microedition.midlet.MIDlet' + + // Preserve all native method names and the names of their classes. + + keepclasseswithmembernames 'class * { \ + native ; \ + }' + + // Your midlet 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/gradle/proguard.gradle b/examples/gradle/proguard.gradle new file mode 100644 index 000000000..40ca5bf66 --- /dev/null +++ b/examples/gradle/proguard.gradle @@ -0,0 +1,91 @@ +// +// This Gradle build file illustrates how to process ProGuard itself. +// Configuration files for typical applications will be very similar. +// Usage: +// gradle -b proguard.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + // We'll filter out the Ant classes, Gradle classes, and WTK classes, keeping + // everything else. + + injars '../../lib/proguard.jar', filter: '!proguard/ant/**,!proguard/gradle/**,!proguard/wtk/**' + outjars 'proguard_out.jar' + + libraryjars '/lib/rt.jar' + + // Write out an obfuscation mapping file, for de-obfuscating any stack traces + // later on, or for incremental obfuscation of extensions. + + printmapping 'proguard.map' + + // Allow methods with the same signature, except for the return type, + // to get the same obfuscation name. + + overloadaggressively + + // Put all obfuscated classes into the nameless root package. + + repackageclasses '' + + // Allow classes and class members to be made public. + + allowaccessmodification + + // The entry point: ProGuard and its main method. + + keep 'public class proguard.ProGuard { \ + public static void main(java.lang.String[]); \ + }' + + // If you want to preserve the Ant task as well, you'll have to specify the + // main ant.jar. + + //libraryjars '/usr/local/java/ant/lib/ant.jar' + //adaptresourcefilecontents 'proguard/ant/task.properties' + // + //keep allowobfuscation: true, 'class proguard.ant.*' + //keepclassmembers 'public class proguard.ant.* { \ + // (org.apache.tools.ant.Project); \ + // public void set*(***); \ + // public void add*(***); \ + //}' + + // If you want to preserve the Gradle task, you'll have to specify the Gradle + // jars. + + //libraryjars '/usr/local/java/gradle/lib/plugins/gradle-plugins-1.3.jar' + //libraryjars '/usr/local/java/gradle/lib/gradle-base-services-1.3.jar' + //libraryjars '/usr/local/java/gradle/lib/gradle-core-1.3.jar' + //libraryjars '/usr/local/java/gradle/lib/groovy-all-1.8.6.jar' + + //keep 'public class proguard.gradle.* { \ + // public *; \ + //}' + + // If you want to preserve the WTK obfuscation plug-in, you'll have to specify + // the kenv.zip file. + + //libraryjars '/usr/local/java/wtk2.5.2/wtklib/kenv.zip' + //keep 'public class proguard.wtk.ProGuardObfuscator' +} diff --git a/examples/gradle/proguardall.gradle b/examples/gradle/proguardall.gradle new file mode 100644 index 000000000..68f8d585f --- /dev/null +++ b/examples/gradle/proguardall.gradle @@ -0,0 +1,93 @@ +// +// This Gradle build file illustrates how to process ProGuard +// (including its main application, its GUI, its Ant task, and its WTK plugin), +// and the ReTrace tool, all in one go. +// Configuration files for typical applications will be very similar. +// Usage: +// gradle -b proguardall.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + // We'll read all jars from the lib directory, process them, and write the + // processed jars to a new out directory. + + injars '../../lib' + outjars 'out' + + // You may have to adapt the paths below. + + libraryjars '/lib/rt.jar' + libraryjars '/usr/local/java/ant/lib/ant.jar' + libraryjars '/usr/local/java/gradle/lib/plugins/gradle-plugins-1.3.jar' + libraryjars '/usr/local/java/gradle/lib/gradle-base-services-1.3.jar' + libraryjars '/usr/local/java/gradle/lib/gradle-core-1.3.jar' + libraryjars '/usr/local/java/gradle/lib/groovy-all-1.8.6.jar' + libraryjars '/usr/local/java/wtk2.5.2/wtklib/kenv.zip' + + // Allow methods with the same signature, except for the return type, + // to get the same obfuscation name. + + overloadaggressively + + // Put all obfuscated classes into the nameless root package. + + repackageclasses '' + + // Adapt the names and contents of the resource files. + + adaptresourcefilenames '**.properties,**.gif,**.jpg' + adaptresourcefilecontents 'proguard/ant/task.properties' + + // The main entry points. + + keep 'public class proguard.ProGuard { \ + public static void main(java.lang.String[]); \ + }' + + keep 'public class proguard.gui.ProGuardGUI { \ + public static void main(java.lang.String[]); \ + }' + + keep 'public class proguard.retrace.ReTrace { \ + public static void main(java.lang.String[]); \ + }' + + // If we have ant.jar, we can properly process the Ant task. + + keep allowobfuscation: true, 'class proguard.ant.*' + keepclassmembers 'public class proguard.ant.* { \ + (org.apache.tools.ant.Project); \ + public void set*(***); \ + public void add*(***); \ + }' + + // If we have the Gradle jars, we can properly process the Gradle task. + + keep 'public class proguard.gradle.* { \ + public *; \ + }' + + // If we have kenv.zip, we can process the J2ME WTK plugin. + + keep 'public class proguard.wtk.ProGuardObfuscator' +} diff --git a/examples/gradle/proguardgui.gradle b/examples/gradle/proguardgui.gradle new file mode 100644 index 000000000..ec820580b --- /dev/null +++ b/examples/gradle/proguardgui.gradle @@ -0,0 +1,72 @@ +// +// This Gradle build file illustrates how to process the ProGuard GUI. +// Configuration files for typical applications will be very similar. +// Usage: +// gradle -b proguardgui.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + // The input jars will be merged in a single output jar. + // We'll filter out the Ant classes, Gradle classes, and WTK classes, keeping + // everything else. + + injars '../../lib/proguardgui.jar' + injars '../../lib/proguard.jar', filter: '!META-INF/**,!proguard/ant/**,!proguard/gradle/**,!proguard/wtk/**' + injars '../../lib/retrace.jar', filter: '!META-INF/**' + outjars 'proguardgui_out.jar' + + libraryjars '/lib/rt.jar' + + // If we wanted to reuse the previously obfuscated proguard_out.jar, we could + // perform incremental obfuscation based on its mapping file, and only keep the + // additional GUI files instead of all files. + + //applymapping 'proguard.map' + //injars '../../lib/proguardgui.jar' + //outjars 'proguardgui_out.jar' + //libraryjars '../../lib/proguard.jar', filter: '!proguard/ant/**,!proguard/wtk/**' + //libraryjars '../../lib/retrace.jar' + //libraryjars '/lib/rt.jar' + + + // Allow methods with the same signature, except for the return type, + // to get the same obfuscation name. + + overloadaggressively + + // Put all obfuscated classes into the nameless root package. + + repackageclasses '' + + // Adapt the names of resource files, based on the corresponding obfuscated + // class names. Notably, in this case, the GUI resource properties file will + // have to be renamed. + + adaptresourcefilenames '**.properties,**.gif,**.jpg' + + // The entry point: ProGuardGUI and its main method. + + keep 'public class proguard.gui.ProGuardGUI { \ + public static void main(java.lang.String[]); \ + }' +} diff --git a/examples/gradle/retrace.gradle b/examples/gradle/retrace.gradle new file mode 100644 index 000000000..38372bf86 --- /dev/null +++ b/examples/gradle/retrace.gradle @@ -0,0 +1,64 @@ +// +// This Gradle build file illustrates how to process the ReTrace tool. +// Configuration files for typical applications will be very similar. +// Usage: +// gradle -b retrace.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + // The input jars will be merged in a single output jar. + // We'll filter out the Ant and WTK classes. + + injars '../../lib/retrace.jar' + injars '../../lib/proguard.jar(!META-INF/MANIFEST.MF,' + !proguard/ant/**,!proguard/wtk/**) + outjars 'retrace_out.jar' + + libraryjars '/lib/rt.jar' + + // If we wanted to reuse the previously obfuscated proguard_out.jar, we could + // perform incremental obfuscation based on its mapping file, and only keep the + // additional ReTrace files instead of all files. + + //applymapping 'proguard.map' + //outjars 'retrace_out.jar', filter: 'proguard/retrace/**' + + // Allow methods with the same signature, except for the return type, + // to get the same obfuscation name. + + overloadaggressively + + // Put all obfuscated classes into the nameless root package. + + repackageclasses '' + + // Allow classes and class members to be made public. + + allowaccessmodification + + // The entry point: ReTrace and its main method. + + keep 'public class proguard.retrace.ReTrace { \ + public static void main(java.lang.String[]); \ + }' +} diff --git a/examples/gradle/scala.gradle b/examples/gradle/scala.gradle new file mode 100644 index 000000000..6c8e4e265 --- /dev/null +++ b/examples/gradle/scala.gradle @@ -0,0 +1,153 @@ +// +// This Gradle build file illustrates how to process Scala +// applications, including the Scala runtime. +// Usage: +// gradle -b scala.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + + injars 'in.jar' + injars '/usr/local/java/scala-2.9.1/lib/scala-library.jar' + //injars '/usr/local/java/scala-2.9.1/lib/scala-compiler.jar', filter: '!META-INF/MANIFEST.MF' + //injars '/usr/local/java/scala-2.9.1/lib/jline.jar', filter: '!META-INF/MANIFEST.MF' + outjars 'out.jar' + + libraryjars '/lib/rt.jar' + //libraryjars '/usr/local/java/ant/lib/ant.jar' + //... + + // Ignore some compiler artefacts. + + dontwarn 'scala.**' + + // Save the obfuscation mapping to a file, so you can de-obfuscate any stack + // traces later on. Keep a fixed source file attribute and all line number + // tables to get line numbers in the stack traces. + // You can comment this out if you're not interested in stack traces. + + printmapping 'out.map' + renamesourcefileattribute 'SourceFile' + keepattributes 'SourceFile,LineNumberTable' + + // Preserve all annotations. + + keepattributes '*Annotation*' + + // You can print out the seeds that are matching the keep options below. + + //printseeds 'out.seeds' + + // Preserve all public applications. + + keepclasseswithmembers 'public class * { \ + public static void main(java.lang.String[]); \ + }' + + // Preserve some classes and class members that are accessed by means of + // introspection. + + keep 'class * implements org.xml.sax.EntityResolver' + + keepclassmembers 'class * { \ + ** MODULE$; \ + }' + + keepclassmembernames 'class scala.concurrent.forkjoin.ForkJoinPool { \ + long eventCount; \ + int workerCounts; \ + int runControl; \ + scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode syncStack; \ + scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode spareStack; \ + }' + + keepclassmembernames 'class scala.concurrent.forkjoin.ForkJoinWorkerThread { \ + int base; \ + int sp; \ + int runState; \ + }' + + keepclassmembernames 'class scala.concurrent.forkjoin.ForkJoinTask { \ + int status; \ + }' + + keepclassmembernames 'class scala.concurrent.forkjoin.LinkedTransferQueue { \ + scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference head; \ + scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference tail; \ + scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference cleanMe; \ + }' + + // Preserve some classes and class members that are accessed by means of + // introspection in the Scala compiler library, if it is processed as well. + + //keep 'class * implements jline.Completor' + //keep 'class * implements jline.Terminal' + + //keep 'class scala.tools.nsc.Global' + + //keepclasseswithmembers 'class * { \ + // (scala.tools.nsc.Global); \ + //}' + + //keepclassmembers 'class * { \ + // *** scala_repl_value(); \ + // *** scala_repl_result(); \ + //}' + + // Preserve all native method names and the names of their classes. + + keepclasseswithmembernames 'class * { \ + native ; \ + }' + + // Preserve the special static methods that are required in all enumeration + // classes. + + keepclassmembers 'class * extends java.lang.Enum { \ + public static **[] values(); \ + public static ** valueOf(java.lang.String); \ + }' + + // Explicitly preserve all serialization members. The Serializable interface + // is only a marker interface, so it wouldn't save them. + // You can comment this out if your application doesn't use serialization. + // If your code contains serializable classes that have to be backward + // compatible, please refer to the manual. + + 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/gradle/servlets.gradle b/examples/gradle/servlets.gradle new file mode 100644 index 000000000..bac72a40e --- /dev/null +++ b/examples/gradle/servlets.gradle @@ -0,0 +1,91 @@ +// +// This Gradle build file illustrates how to process servlets. +// Usage: +// gradle -b servlets.gradle proguard +// + +// Tell Gradle where to find the ProGuard task. + +buildscript { + repositories { + flatDir dirs: '../../lib' + } + dependencies { + classpath ':proguard' + } +} + +// Define a ProGuard task. + +task proguard(type: proguard.gradle.ProGuardTask) { + + // You should probably import a more compact ProGuard-style configuration + // file for all static settings, but we're specifying them all here, for + // the sake of the example. + //configuration 'configuration.pro' + + // Specify the input jars, output jars, and library jars. + + injars 'in.jar' + outjars 'out.jar' + + libraryjars '/lib/rt.jar' + libraryjars '/usr/local/java/servlet/servlet.jar' + + // Save the obfuscation mapping to a file, so you can de-obfuscate any stack + // traces later on. Keep a fixed source file attribute and all line number + // tables to get line numbers in the stack traces. + // You can comment this out if you're not interested in stack traces. + + printmapping 'out.map' + renamesourcefileattribute 'SourceFile' + keepattributes 'SourceFile,LineNumberTable' + + // Preserve all annotations. + + keepattributes '*Annotation*' + + // You can print out the seeds that are matching the keep options below. + + //printseeds 'out.seeds' + + // Preserve all public servlets. + + keep 'public class * implements javax.servlet.Servlet' + + // Preserve all native method names and the names of their classes. + + keepclasseswithmembernames 'class * { \ + native ; \ + }' + + // Preserve the special static methods that are required in all enumeration + // classes. + + keepclassmembers 'class * extends java.lang.Enum { \ + public static **[] values(); \ + public static ** valueOf(java.lang.String); \ + }' + + // Explicitly preserve all serialization members. The Serializable interface + // is only a marker interface, so it wouldn't save them. + // You can comment this out if your library doesn't use serialization. + // If your code contains serializable classes that have to be backward + // compatible, please refer to the manual. + + 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/library.pro b/examples/library.pro new file mode 100644 index 000000000..b81208258 --- /dev/null +++ b/examples/library.pro @@ -0,0 +1,79 @@ +# +# This ProGuard configuration file illustrates how to process a program +# library, such that it remains usable as a library. +# Usage: +# java -jar proguard.jar @library.pro +# + +# Specify the input jars, output jars, and library jars. +# In this case, the input jar is the program library that we want to process. + +-injars in.jar +-outjars out.jar + +-libraryjars /lib/rt.jar + +# Save the obfuscation mapping to a file, so we can de-obfuscate any stack +# traces later on. Keep a fixed source file attribute and all line number +# tables to get line numbers in the stack traces. +# You can comment this out if you're not interested in stack traces. + +-printmapping out.map +-keepparameternames +-renamesourcefileattribute SourceFile +-keepattributes Exceptions,InnerClasses,Signature,Deprecated, + SourceFile,LineNumberTable,EnclosingMethod + +# Preserve all annotations. + +-keepattributes *Annotation* + +# Preserve all public classes, and their public and protected fields and +# methods. + +-keep public class * { + public protected *; +} + +# Preserve all .class method names. + +-keepclassmembernames class * { + java.lang.Class class$(java.lang.String); + java.lang.Class class$(java.lang.String, boolean); +} + +# Preserve all native method names and the names of their classes. + +-keepclasseswithmembernames class * { + native ; +} + +# Preserve the special static methods that are required in all enumeration +# classes. + +-keepclassmembers class * extends java.lang.Enum { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Explicitly preserve all serialization members. The Serializable interface +# is only a marker interface, so it wouldn't save them. +# You can comment this out if your library doesn't use serialization. +# If your code contains serializable classes that have to be backward +# compatible, please refer to the manual. + +-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 library 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/midlets.pro b/examples/midlets.pro new file mode 100644 index 000000000..1383980a8 --- /dev/null +++ b/examples/midlets.pro @@ -0,0 +1,67 @@ +# +# This ProGuard configuration file illustrates how to process J2ME midlets. +# Usage: +# java -jar proguard.jar @midlets.pro +# + +# Specify the input jars, output jars, and library jars. + +-injars in.jar +-outjars out.jar + +-libraryjars /usr/local/java/wtk2.5.2/lib/midpapi20.jar +-libraryjars /usr/local/java/wtk2.5.2/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. + +-overloadaggressively + +# Put all obfuscated classes into the nameless root package. + +-repackageclasses '' + +# Allow classes and class members to be made public. + +-allowaccessmodification + +# On Windows, you can't use mixed case class names, +# should you still want to use the preverify tool. +# +# -dontusemixedcaseclassnames + +# Save the obfuscation mapping to a file, so you can de-obfuscate any stack +# traces later on. + +-printmapping out.map + +# You can keep a fixed source file attribute and all line number tables to +# get stack traces with line numbers. + +#-renamesourcefileattribute SourceFile +#-keepattributes SourceFile,LineNumberTable + +# You can print out the seeds that are matching the keep options below. + +#-printseeds out.seeds + +# Preserve all public midlets. + +-keep public class * extends javax.microedition.midlet.MIDlet + +# Preserve all native method names and the names of their classes. + +-keepclasseswithmembernames class * { + native ; +} + +# Your midlet 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/proguard.pro b/examples/proguard.pro new file mode 100644 index 000000000..aec64d30e --- /dev/null +++ b/examples/proguard.pro @@ -0,0 +1,70 @@ +# +# This ProGuard configuration file illustrates how to process ProGuard itself. +# Configuration files for typical applications will be very similar. +# Usage: +# java -jar proguard.jar @proguard.pro +# + +# Specify the input jars, output jars, and library jars. +# We'll filter out the Ant classes, Gradle classes, and WTK classes, keeping +# everything else. + +-injars ../lib/proguard.jar(!proguard/ant/**,!proguard/gradle/**,!proguard/wtk/**) +-outjars proguard_out.jar + +-libraryjars /lib/rt.jar + +# Write out an obfuscation mapping file, for de-obfuscating any stack traces +# later on, or for incremental obfuscation of extensions. + +-printmapping proguard.map + +# Allow methods with the same signature, except for the return type, +# to get the same obfuscation name. + +-overloadaggressively + +# Put all obfuscated classes into the nameless root package. + +-repackageclasses '' + +# Allow classes and class members to be made public. + +-allowaccessmodification + +# The entry point: ProGuard and its main method. + +-keep public class proguard.ProGuard { + public static void main(java.lang.String[]); +} + +# If you want to preserve the Ant task as well, you'll have to specify the +# main ant.jar. + +#-libraryjars /usr/local/java/ant/lib/ant.jar +#-adaptresourcefilecontents proguard/ant/task.properties +# +#-keep,allowobfuscation class proguard.ant.* +#-keepclassmembers public class proguard.ant.* { +# (org.apache.tools.ant.Project); +# public void set*(***); +# public void add*(***); +#} + +# If you want to preserve the Gradle task, you'll have to specify the Gradle +# jars. + +#-libraryjars /usr/local/java/gradle/lib/plugins/gradle-plugins-1.3.jar +#-libraryjars /usr/local/java/gradle/lib/gradle-base-services-1.3.jar +#-libraryjars /usr/local/java/gradle/lib/gradle-core-1.3.jar +#-libraryjars /usr/local/java/gradle/lib/groovy-all-1.8.6.jar + +#-keep public class proguard.gradle.* { +# public *; +#} + +# If you want to preserve the WTK obfuscation plug-in, you'll have to specify +# the kenv.zip file. + +#-libraryjars /usr/local/java/wtk2.5.2/wtklib/kenv.zip +#-keep public class proguard.wtk.ProGuardObfuscator diff --git a/examples/proguardall.pro b/examples/proguardall.pro new file mode 100644 index 000000000..f722053b9 --- /dev/null +++ b/examples/proguardall.pro @@ -0,0 +1,72 @@ +# +# This ProGuard configuration file illustrates how to process ProGuard +# (including its main application, its GUI, its Ant task, and its WTK plugin), +# and the ReTrace tool, all in one go. +# Configuration files for typical applications will be very similar. +# Usage: +# java -jar proguard.jar @proguardall.pro +# + +# Specify the input jars, output jars, and library jars. +# We'll read all jars from the lib directory, process them, and write the +# processed jars to a new out directory. + +-injars ../lib +-outjars out + +# You may have to adapt the paths below. + +-libraryjars /lib/rt.jar +-libraryjars /usr/local/java/ant/lib/ant.jar +-libraryjars /usr/local/java/gradle/lib/plugins/gradle-plugins-1.3.jar +-libraryjars /usr/local/java/gradle/lib/gradle-base-services-1.3.jar +-libraryjars /usr/local/java/gradle/lib/gradle-core-1.3.jar +-libraryjars /usr/local/java/gradle/lib/groovy-all-1.8.6.jar +-libraryjars /usr/local/java/wtk2.5.2/wtklib/kenv.zip + +# Allow methods with the same signature, except for the return type, +# to get the same obfuscation name. + +-overloadaggressively + +# Put all obfuscated classes into the nameless root package. + +-repackageclasses '' + +# Adapt the names and contents of the resource files. + +-adaptresourcefilenames **.properties,**.gif,**.jpg +-adaptresourcefilecontents proguard/ant/task.properties + +# The main entry points. + +-keep public class proguard.ProGuard { + public static void main(java.lang.String[]); +} + +-keep public class proguard.gui.ProGuardGUI { + public static void main(java.lang.String[]); +} + +-keep public class proguard.retrace.ReTrace { + public static void main(java.lang.String[]); +} + +# If we have ant.jar, we can properly process the Ant task. + +-keep,allowobfuscation class proguard.ant.* +-keepclassmembers public class proguard.ant.* { + (org.apache.tools.ant.Project); + public void set*(***); + public void add*(***); +} + +# If we have the Gradle jars, we can properly process the Gradle task. + +-keep public class proguard.gradle.* { + public *; +} + +# If we have kenv.zip, we can process the J2ME WTK plugin. + +-keep public class proguard.wtk.ProGuardObfuscator diff --git a/examples/proguardgui.pro b/examples/proguardgui.pro new file mode 100644 index 000000000..78f67dbf6 --- /dev/null +++ b/examples/proguardgui.pro @@ -0,0 +1,51 @@ +# +# This ProGuard configuration file illustrates how to process the ProGuard GUI. +# Configuration files for typical applications will be very similar. +# Usage: +# java -jar proguard.jar @proguardgui.pro +# + +# Specify the input jars, output jars, and library jars. +# The input jars will be merged in a single output jar. +# We'll filter out the Ant classes, Gradle classes, and WTK classes, keeping +# everything else. + +-injars ../lib/proguardgui.jar +-injars ../lib/proguard.jar(!META-INF/**,!proguard/ant/**,!proguard/gradle/**,!proguard/wtk/**) +-injars ../lib/retrace.jar (!META-INF/**) +-outjars proguardgui_out.jar + +-libraryjars /lib/rt.jar + +# If we wanted to reuse the previously obfuscated proguard_out.jar, we could +# perform incremental obfuscation based on its mapping file, and only keep the +# additional GUI files instead of all files. + +#-applymapping proguard.map +#-injars ../lib/proguardgui.jar +#-outjars proguardgui_out.jar +#-libraryjars ../lib/proguard.jar(!proguard/ant/**,!proguard/wtk/**) +#-libraryjars ../lib/retrace.jar +#-libraryjars /lib/rt.jar + + +# Allow methods with the same signature, except for the return type, +# to get the same obfuscation name. + +-overloadaggressively + +# Put all obfuscated classes into the nameless root package. + +-repackageclasses '' + +# Adapt the names of resource files, based on the corresponding obfuscated +# class names. Notably, in this case, the GUI resource properties file will +# have to be renamed. + +-adaptresourcefilenames **.properties,**.gif,**.jpg + +# The entry point: ProGuardGUI and its main method. + +-keep public class proguard.gui.ProGuardGUI { + public static void main(java.lang.String[]); +} diff --git a/examples/retrace.pro b/examples/retrace.pro new file mode 100644 index 000000000..39f5a95c5 --- /dev/null +++ b/examples/retrace.pro @@ -0,0 +1,43 @@ +# +# This ProGuard configuration file illustrates how to process the ReTrace tool. +# Configuration files for typical applications will be very similar. +# Usage: +# java -jar proguard.jar @retrace.pro +# + +# Specify the input jars, output jars, and library jars. +# The input jars will be merged in a single output jar. +# We'll filter out the Ant and WTK classes. + +-injars ../lib/retrace.jar +-injars ../lib/proguard.jar(!META-INF/MANIFEST.MF, + !proguard/ant/**,!proguard/wtk/**) +-outjars retrace_out.jar + +-libraryjars /lib/rt.jar + +# If we wanted to reuse the previously obfuscated proguard_out.jar, we could +# perform incremental obfuscation based on its mapping file, and only keep the +# additional ReTrace files instead of all files. + +#-applymapping proguard.map +#-outjars retrace_out.jar(proguard/retrace/**) + +# Allow methods with the same signature, except for the return type, +# to get the same obfuscation name. + +-overloadaggressively + +# Put all obfuscated classes into the nameless root package. + +-repackageclasses '' + +# Allow classes and class members to be made public. + +-allowaccessmodification + +# The entry point: ReTrace and its main method. + +-keep public class proguard.retrace.ReTrace { + public static void main(java.lang.String[]); +} diff --git a/examples/scala.pro b/examples/scala.pro new file mode 100644 index 000000000..658fc7727 --- /dev/null +++ b/examples/scala.pro @@ -0,0 +1,132 @@ +# +# This ProGuard configuration file illustrates how to process Scala +# applications, including the Scala runtime. +# Usage: +# java -jar proguard.jar @scala.pro +# + +# Specify the input jars, output jars, and library jars. + +-injars in.jar +-injars /usr/local/java/scala-2.9.1/lib/scala-library.jar +#-injars /usr/local/java/scala-2.9.1/lib/scala-compiler.jar(!META-INF/MANIFEST.MF) +#-injars /usr/local/java/scala-2.9.1/lib/jline.jar(!META-INF/MANIFEST.MF) +-outjars out.jar + +-libraryjars /lib/rt.jar +#-libraryjars /usr/local/java/ant/lib/ant.jar +#... + +# Ignore some compiler artefacts. + +-dontwarn scala.** + +# Save the obfuscation mapping to a file, so you can de-obfuscate any stack +# traces later on. Keep a fixed source file attribute and all line number +# tables to get line numbers in the stack traces. +# You can comment this out if you're not interested in stack traces. + +-printmapping out.map +-renamesourcefileattribute SourceFile +-keepattributes SourceFile,LineNumberTable + +# Preserve all annotations. + +-keepattributes *Annotation* + +# You can print out the seeds that are matching the keep options below. + +#-printseeds out.seeds + +# Preserve all public applications. + +-keepclasseswithmembers public class * { + public static void main(java.lang.String[]); +} + +# Preserve some classes and class members that are accessed by means of +# introspection. + +-keep class * implements org.xml.sax.EntityResolver + +-keepclassmembers class * { + ** MODULE$; +} + +-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinPool { + long eventCount; + int workerCounts; + int runControl; + scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode syncStack; + scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode spareStack; +} + +-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinWorkerThread { + int base; + int sp; + int runState; +} + +-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinTask { + int status; +} + +-keepclassmembernames class scala.concurrent.forkjoin.LinkedTransferQueue { + scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference head; + scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference tail; + scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference cleanMe; +} + +# Preserve some classes and class members that are accessed by means of +# introspection in the Scala compiler library, if it is processed as well. + +#-keep class * implements jline.Completor +#-keep class * implements jline.Terminal + +#-keep class scala.tools.nsc.Global + +#-keepclasseswithmembers class * { +# (scala.tools.nsc.Global); +#} + +#-keepclassmembers class * { +# *** scala_repl_value(); +# *** scala_repl_result(); +#} + +# Preserve all native method names and the names of their classes. + +-keepclasseswithmembernames class * { + native ; +} + +# Preserve the special static methods that are required in all enumeration +# classes. + +-keepclassmembers class * extends java.lang.Enum { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Explicitly preserve all serialization members. The Serializable interface +# is only a marker interface, so it wouldn't save them. +# You can comment this out if your application doesn't use serialization. +# If your code contains serializable classes that have to be backward +# compatible, please refer to the manual. + +-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/servlets.pro b/examples/servlets.pro new file mode 100644 index 000000000..b42b2e4e2 --- /dev/null +++ b/examples/servlets.pro @@ -0,0 +1,70 @@ +# +# This ProGuard configuration file illustrates how to process servlets. +# Usage: +# java -jar proguard.jar @servlets.pro +# + +# Specify the input jars, output jars, and library jars. + +-injars in.jar +-outjars out.jar + +-libraryjars /lib/rt.jar +-libraryjars /usr/local/java/servlet/servlet.jar + +# Save the obfuscation mapping to a file, so you can de-obfuscate any stack +# traces later on. Keep a fixed source file attribute and all line number +# tables to get line numbers in the stack traces. +# You can comment this out if you're not interested in stack traces. + +-printmapping out.map +-renamesourcefileattribute SourceFile +-keepattributes SourceFile,LineNumberTable + +# Preserve all annotations. + +-keepattributes *Annotation* + +# You can print out the seeds that are matching the keep options below. + +#-printseeds out.seeds + +# Preserve all public servlets. + +-keep public class * implements javax.servlet.Servlet + +# Preserve all native method names and the names of their classes. + +-keepclasseswithmembernames class * { + native ; +} + +# Preserve the special static methods that are required in all enumeration +# classes. + +-keepclassmembers class * extends java.lang.Enum { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Explicitly preserve all serialization members. The Serializable interface +# is only a marker interface, so it wouldn't save them. +# You can comment this out if your library doesn't use serialization. +# If your code contains serializable classes that have to be backward +# compatible, please refer to the manual. + +-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/src/proguard/ArgumentWordReader.java b/src/proguard/ArgumentWordReader.java new file mode 100644 index 000000000..efe8e6e47 --- /dev/null +++ b/src/proguard/ArgumentWordReader.java @@ -0,0 +1,111 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + + +/** + * A WordReader that returns words from an argument list. + * Single arguments are split into individual words if necessary. + * + * @author Eric Lafortune + */ +public class ArgumentWordReader extends WordReader +{ + private final String[] arguments; + + private int index = 0; + + +// /** +// * Creates a new ArgumentWordReader for the given arguments. +// */ +// public ArgumentWordReader(String[] arguments) +// { +// this(arguments, null); +// } +// +// + /** + * Creates a new ArgumentWordReader for the given arguments, with the + * given base directory. + */ + public ArgumentWordReader(String[] arguments, File baseDir) + { + super(baseDir); + + this.arguments = arguments; + } + + + // Implementations for WordReader. + + protected String nextLine() throws IOException + { + return index < arguments.length ? + arguments[index++] : + null; + } + + + protected String lineLocationDescription() + { + return "argument number " + index; + } + + + /** + * Test application that prints out the individual words of + * the argument list. + */ + public static void main(String[] args) { + + try + { + WordReader reader = new ArgumentWordReader(args, null); + + try + { + while (true) + { + String word = reader.nextWord(false); + if (word == null) + System.exit(-1); + + System.err.println("["+word+"]"); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + finally + { + reader.close(); + } + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/ClassPath.java b/src/proguard/ClassPath.java new file mode 100644 index 000000000..3d7d1195e --- /dev/null +++ b/src/proguard/ClassPath.java @@ -0,0 +1,94 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.*; + + +/** + * This class represents a class path, as a list of ClassPathEntry objects. + * + * @author Eric Lafortune + */ +public class ClassPath +{ + private final List classPathEntries = new ArrayList(); + + + /** + * Returns whether the class path contains any output entries. + */ + public boolean hasOutput() + { + for (int index = 0; index < classPathEntries.size(); index++) + { + if (((ClassPathEntry)classPathEntries.get(index)).isOutput()) + { + return true; + } + } + + return false; + } + + + // Delegates to List. + + public void clear() + { + classPathEntries.clear(); + } + + public void add(int index, ClassPathEntry classPathEntry) + { + classPathEntries.add(index, classPathEntry); + } + + public boolean add(ClassPathEntry classPathEntry) + { + return classPathEntries.add(classPathEntry); + } + + public boolean addAll(ClassPath classPath) + { + return classPathEntries.addAll(classPath.classPathEntries); + } + + public ClassPathEntry get(int index) + { + return (ClassPathEntry)classPathEntries.get(index); + } + + public ClassPathEntry remove(int index) + { + return (ClassPathEntry)classPathEntries.remove(index); + } + + public boolean isEmpty() + { + return classPathEntries.isEmpty(); + } + + public int size() + { + return classPathEntries.size(); + } +} diff --git a/src/proguard/ClassPathEntry.java b/src/proguard/ClassPathEntry.java new file mode 100644 index 000000000..705195528 --- /dev/null +++ b/src/proguard/ClassPathEntry.java @@ -0,0 +1,282 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.ListUtil; + +import java.io.*; +import java.util.List; + + +/** + * 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/classes, jars, wars, ears, and zips. + * + * @author Eric Lafortune + */ +public class ClassPathEntry +{ + private File file; + private boolean output; + private List filter; + private List jarFilter; + private List warFilter; + private List earFilter; + private List zipFilter; + + + /** + * Creates a new ClassPathEntry with the given file and output flag. + */ + public ClassPathEntry(File file, boolean isOutput) + { + this.file = file; + this.output = isOutput; + } + + + /** + * Returns the path name of the entry. + */ + public String getName() + { + try + { + return file.getCanonicalPath(); + } + catch (IOException ex) + { + return file.getPath(); + } + } + + + /** + * Returns the file. + */ + public File getFile() + { + return file; + } + + + /** + * Sets the file. + */ + public void setFile(File file) + { + this.file = file; + } + + + /** + * Returns whether this data entry is an output entry. + */ + public boolean isOutput() + { + return output; + } + + + /** + * Specifies whether this data entry is an output entry. + */ + public void setOutput(boolean output) + { + this.output = output; + } + + + /** + * Returns whether this data entry is a jar file. + */ + public boolean isJar() + { + return hasExtension(".jar"); + } + + + /** + * Returns whether this data entry is a war file. + */ + public boolean isWar() + { + return hasExtension(".war"); + } + + + /** + * Returns whether this data entry is a ear file. + */ + public boolean isEar() + { + return hasExtension(".ear"); + } + + + /** + * Returns whether this data entry is a zip file. + */ + public boolean isZip() + { + return hasExtension(".zip"); + } + + + /** + * Returns whether this data entry has the given extension. + */ + private boolean hasExtension(String extension) + { + return endsWithIgnoreCase(file.getPath(), 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); + } + + + /** + * Returns the name filter that is applied to bottom-level files in this entry. + */ + public List getFilter() + { + return filter; + } + + /** + * Sets the name filter that is applied to bottom-level files in this entry. + */ + public void setFilter(List filter) + { + this.filter = filter == null || filter.size() == 0 ? null : filter; + } + + + /** + * Returns the name filter that is applied to jar files in this entry, if any. + */ + public List getJarFilter() + { + return jarFilter; + } + + /** + * Sets the name filter that is applied to jar files in this entry, if any. + */ + public void setJarFilter(List filter) + { + this.jarFilter = filter == null || filter.size() == 0 ? null : filter; + } + + + /** + * Returns the name filter that is applied to war files in this entry, if any. + */ + public List getWarFilter() + { + return warFilter; + } + + /** + * Sets the name filter that is applied to war files in this entry, if any. + */ + public void setWarFilter(List filter) + { + this.warFilter = filter == null || filter.size() == 0 ? null : filter; + } + + + /** + * Returns the name filter that is applied to ear files in this entry, if any. + */ + public List getEarFilter() + { + return earFilter; + } + + /** + * Sets the name filter that is applied to ear files in this entry, if any. + */ + public void setEarFilter(List filter) + { + this.earFilter = filter == null || filter.size() == 0 ? null : filter; + } + + + /** + * Returns the name filter that is applied to zip files in this entry, if any. + */ + public List getZipFilter() + { + return zipFilter; + } + + /** + * Sets the name filter that is applied to zip files in this entry, if any. + */ + public void setZipFilter(List filter) + { + this.zipFilter = filter == null || filter.size() == 0 ? null : filter; + } + + + // Implementations for Object. + + public String toString() + { + String string = getName(); + + if (filter != null || + jarFilter != null || + warFilter != null || + earFilter != null || + zipFilter != null) + { + string += + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + + (zipFilter != null ? ListUtil.commaSeparatedString(zipFilter, true) : "") + + ConfigurationConstants.SEPARATOR_KEYWORD + + (earFilter != null ? ListUtil.commaSeparatedString(earFilter, true) : "") + + ConfigurationConstants.SEPARATOR_KEYWORD + + (warFilter != null ? ListUtil.commaSeparatedString(warFilter, true) : "") + + ConfigurationConstants.SEPARATOR_KEYWORD + + (jarFilter != null ? ListUtil.commaSeparatedString(jarFilter, true) : "") + + ConfigurationConstants.SEPARATOR_KEYWORD + + (filter != null ? ListUtil.commaSeparatedString(filter, true) : "") + + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD; + } + + return string; + } +} diff --git a/src/proguard/ClassSpecification.java b/src/proguard/ClassSpecification.java new file mode 100644 index 000000000..485c0b033 --- /dev/null +++ b/src/proguard/ClassSpecification.java @@ -0,0 +1,259 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.*; + +/** + * This class stores a specification of classes and possibly class members. + * The specification is template-based: the class names and class member names + * and descriptors can contain wildcards. Classes can be specified explicitly, + * or as extensions or implementations in the class hierarchy. + * + * @author Eric Lafortune + */ +public class ClassSpecification implements Cloneable +{ + 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; + + + /** + * Creates a new ClassSpecification for all possible classes, without + * comments or class members. + */ + public ClassSpecification() + { + this(null, + 0, + 0, + null, + null, + null, + 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 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 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 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(String comments, + int requiredSetAccessFlags, + int requiredUnsetAccessFlags, + String annotationType, + String className, + String extendsAnnotationType, + String extendsClassName) + { + this(comments, + requiredSetAccessFlags, + requiredUnsetAccessFlags, + annotationType, + className, + extendsAnnotationType, + extendsClassName, + null, + null); + } + + + /** + * 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 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 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(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.fieldSpecifications = fieldSpecifications; + this.methodSpecifications = methodSpecifications; + } + + + /** + * Specifies to keep the specified field(s) of this option's class(es). + * + * @param fieldSpecification the field specification. + */ + public void addField(MemberSpecification fieldSpecification) + { + if (fieldSpecifications == null) + { + fieldSpecifications = new ArrayList(); + } + + fieldSpecifications.add(fieldSpecification); + } + + + /** + * Specifies to keep the specified method(s) of this option's class(es). + * + * @param methodSpecification the method specification. + */ + public void addMethod(MemberSpecification methodSpecification) + { + if (methodSpecifications == null) + { + methodSpecifications = new ArrayList(); + } + + methodSpecifications.add(methodSpecification); + } + + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (object == null || + this.getClass() != object.getClass()) + { + return false; + } + + ClassSpecification other = (ClassSpecification)object; + return +// (this.comments == null ? other.comments == null : this.comments.equals(other.comments) ) && + (this.requiredSetAccessFlags == other.requiredSetAccessFlags ) && + (this.requiredUnsetAccessFlags == other.requiredUnsetAccessFlags ) && + (this.annotationType == null ? other.annotationType == null : this.annotationType.equals(other.annotationType) ) && + (this.className == null ? other.className == null : this.className.equals(other.className) ) && + (this.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 +// (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() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + return null; + } + } +} diff --git a/src/proguard/ClassSpecificationVisitorFactory.java b/src/proguard/ClassSpecificationVisitorFactory.java new file mode 100644 index 000000000..dc5d71f65 --- /dev/null +++ b/src/proguard/ClassSpecificationVisitorFactory.java @@ -0,0 +1,503 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.attribute.annotation.visitor.*; +import proguard.classfile.attribute.visitor.AllAttributeVisitor; +import proguard.classfile.visitor.*; + +import java.util.List; + +/** + * This factory creates visitors to efficiently travel to specified classes and + * class members. + * + * @author Eric Lafortune + */ +public class ClassSpecificationVisitorFactory +{ + /** + * Constructs a ClassPoolVisitor to efficiently travel to the specified + * classes and class members. + * + * @param keepClassSpecifications the list of KeepClassSpecification + * instances, defining of the classes and + * class members to visit. + * @param classVisitor the ClassVisitor to be applied to matching + * classes. + * @param memberVisitor the MemberVisitor to be applied to matching + * class members. + */ + public static ClassPoolVisitor createClassPoolVisitor(List keepClassSpecifications, + ClassVisitor classVisitor, + MemberVisitor memberVisitor, + boolean shrinking, + boolean optimizing, + boolean obfuscating) + { + MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor(); + + if (keepClassSpecifications != null) + { + for (int index = 0; index < keepClassSpecifications.size(); index++) + { + KeepClassSpecification keepClassSpecification = + (KeepClassSpecification)keepClassSpecifications.get(index); + + if ((shrinking && !keepClassSpecification.allowShrinking) || + (optimizing && !keepClassSpecification.allowOptimization) || + (obfuscating && !keepClassSpecification.allowObfuscation)) + { + multiClassPoolVisitor.addClassPoolVisitor( + createClassPoolVisitor(keepClassSpecification, + 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 memberVisitor the MemberVisitor to be applied to matching + * class members. + */ + public static ClassPoolVisitor createClassPoolVisitor(List classSpecifications, + ClassVisitor classVisitor, + MemberVisitor memberVisitor) + { + MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor(); + + if (classSpecifications != null) + { + for (int index = 0; index < classSpecifications.size(); index++) + { + ClassSpecification classSpecification = + (ClassSpecification)classSpecifications.get(index); + + multiClassPoolVisitor.addClassPoolVisitor( + createClassPoolVisitor(classSpecification, + classVisitor, + memberVisitor)); + } + } + + return multiClassPoolVisitor; + } + + + /** + * Constructs a ClassPoolVisitor to efficiently travel to the specified + * classes and class members. + * + * @param keepClassSpecification the specifications of the class(es) and class + * members to visit. + * @param classVisitor the ClassVisitor to be applied to matching + * classes. + * @param memberVisitor the MemberVisitor to be applied to matching + * class members. + */ + private static ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification, + ClassVisitor classVisitor, + MemberVisitor memberVisitor) + { + // Don't visit the classes if not specified. + if (!keepClassSpecification.markClasses && + !keepClassSpecification.markConditionally) + { + classVisitor = null; + } + + // If specified, let the marker visit the class and its class + // members conditionally. + if (keepClassSpecification.markConditionally) + { + // Combine both visitors. + ClassVisitor composedClassVisitor = + createCombinedClassVisitor(keepClassSpecification, + classVisitor, + memberVisitor); + + // Replace the class visitor. + classVisitor = + createClassMemberTester(keepClassSpecification, + composedClassVisitor); + + // Discard the member visitor, because it has already been included. + memberVisitor = null; + } + + return createClassPoolVisitor((ClassSpecification)keepClassSpecification, + classVisitor, + memberVisitor); + } + + + /** + * 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 classVisitor the ClassVisitor to be applied to matching + * classes. + * @param memberVisitor the MemberVisitor to be applied to matching + * class members. + */ + private static ClassPoolVisitor createClassPoolVisitor(ClassSpecification classSpecification, + ClassVisitor classVisitor, + MemberVisitor memberVisitor) + { + // Combine both visitors. + ClassVisitor composedClassVisitor = + createCombinedClassVisitor(classSpecification, + classVisitor, + memberVisitor); + + // By default, start visiting from the named class name, if specified. + String className = classSpecification.className; + + // 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 && + (extendsAnnotationType != null || + extendsClassName != null || + containsWildCards(className))) + { + composedClassVisitor = + new ClassNameFilter(className, composedClassVisitor); + + // We'll have to visit all classes now. + className = null; + } + + // 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) + { + composedClassVisitor = + new ClassAccessFilter(classSpecification.requiredSetAccessFlags, + classSpecification.requiredUnsetAccessFlags, + composedClassVisitor); + } + + // If it's specified, start visiting from the extended class. + if (extendsAnnotationType != null || + extendsClassName != null) + { + // Start visiting from the extended class. + composedClassVisitor = + new ClassHierarchyTraveler(false, false, false, true, + composedClassVisitor); + + // If specified, only visit extended classes with the right annotation. + if (extendsAnnotationType != null) + { + composedClassVisitor = + new AllAttributeVisitor( + new AllAnnotationVisitor( + new AnnotationTypeFilter(extendsAnnotationType, + new AnnotatedClassVisitor(composedClassVisitor)))); + } + + // If specified, only visit extended classes with matching names. + if (extendsClassName != null) + { + // 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 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; + } + + + /** + * Constructs a ClassVisitor to efficiently travel to the specified class + * members. + * + * @param classSpecification the specifications of the class members to visit. + * @param memberVisitor the MemberVisitor to be applied to matching + * class members. + */ + private static ClassVisitor createClassVisitor(ClassSpecification classSpecification, + MemberVisitor memberVisitor) + { + MultiClassVisitor multiClassVisitor = new MultiClassVisitor(); + + 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 ClassHierarchyTraveler(true, true, false, false, + multiClassVisitor); + } + + + /** + * 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 addMemberVisitors(List memberSpecifications, + boolean isField, + MultiClassVisitor multiClassVisitor, + MemberVisitor memberVisitor) + { + if (memberSpecifications != null) + { + for (int index = 0; index < memberSpecifications.size(); index++) + { + MemberSpecification memberSpecification = + (MemberSpecification)memberSpecifications.get(index); + + multiClassVisitor.addClassVisitor( + createClassVisitor(memberSpecification, + isField, + memberVisitor)); + } + } + } + + + /** + * Constructs a ClassVisitor that conditionally applies the given + * ClassVisitor to all classes that contain the given class members. + */ + private static ClassVisitor createClassMemberTester(ClassSpecification classSpecification, + ClassVisitor classVisitor) + { + // Create a linked list of conditional visitors, for fields and for + // methods. + return createClassMemberTester(classSpecification.fieldSpecifications, + true, + createClassMemberTester(classSpecification.methodSpecifications, + false, + classVisitor)); + } + + + /** + * 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 ClassVisitor createClassMemberTester(List memberSpecifications, + boolean isField, + ClassVisitor classVisitor) + { + // Create a linked list of conditional visitors. + if (memberSpecifications != null) + { + for (int index = 0; index < memberSpecifications.size(); index++) + { + MemberSpecification memberSpecification = + (MemberSpecification)memberSpecifications.get(index); + + classVisitor = + createClassVisitor(memberSpecification, + isField, + new MemberToClassVisitor(classVisitor)); + } + } + + return classVisitor; + } + + + /** + * Creates a new ClassVisitor to efficiently travel to the specified class + * members. + * + * @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 ClassVisitor createClassVisitor(MemberSpecification memberSpecification, + boolean isField, + MemberVisitor memberVisitor) + { + String name = memberSpecification.name; + String descriptor = memberSpecification.descriptor; + + // If name or descriptor are not fully specified, only visit matching + // class members. + boolean fullySpecified = + name != null && + descriptor != null && + !containsWildCards(name) && + !containsWildCards(descriptor); + + if (!fullySpecified) + { + if (descriptor != null) + { + memberVisitor = + new MemberDescriptorFilter(descriptor, memberVisitor); + } + + if (name != null) + { + 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 (memberSpecification.requiredSetAccessFlags != 0 || + memberSpecification.requiredUnsetAccessFlags != 0) + { + 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 ? + (ClassVisitor)new NamedFieldVisitor(name, descriptor, memberVisitor) : + (ClassVisitor)new AllFieldVisitor(memberVisitor) : + fullySpecified ? + (ClassVisitor)new NamedMethodVisitor(name, descriptor, memberVisitor) : + (ClassVisitor)new AllMethodVisitor(memberVisitor); + } + + + // Small utility methods. + + 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); + } +} diff --git a/src/proguard/Configuration.java b/src/proguard/Configuration.java new file mode 100644 index 000000000..471126054 --- /dev/null +++ b/src/proguard/Configuration.java @@ -0,0 +1,331 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; +import java.util.List; + +/** + * The ProGuard configuration. + * + * @see ProGuard + * + * @author Eric Lafortune + */ +public class Configuration +{ + public static final File STD_OUT = new File(""); + + + /////////////////////////////////////////////////////////////////////////// + // Input and output options. + /////////////////////////////////////////////////////////////////////////// + + /** + * A list of input and output entries (jars, wars, ears, zips, and directories). + */ + public ClassPath programJars; + + /** + * A list of library entries (jars, wars, ears, zips, and directories). + */ + public ClassPath libraryJars; + + /** + * Specifies whether to skip non-public library classes while reading + * library jars. + */ + public boolean skipNonPublicLibraryClasses = false; + + /** + * Specifies whether to skip non-public library class members while reading + * library classes. + */ + public boolean skipNonPublicLibraryClassMembers = true; + + /** + * A list of Strings specifying directories to be kept in + * the output directories or the output jars. A null list + * means no directories. An empty list means all directories. The directory + * names may contain "**", "*", or "?" wildcards, and they may be preceded + * by the "!" negator. + */ + public List keepDirectories; + + /** + * Specifies the version number of the output classes, or 0 if the version + * number can be left unchanged. + */ + 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 KeepClassSpecification} instances, whose class names and + * class member names are to be kept from shrinking, optimization, and/or + * obfuscation. + */ + public List keep; + + /** + * An optional output file for listing the kept seeds. + * An empty file name means the standard output. + */ + public File printSeeds; + + /////////////////////////////////////////////////////////////////////////// + // Shrinking options. + /////////////////////////////////////////////////////////////////////////// + + /** + * Specifies whether the code should be shrunk. + */ + public boolean shrink = true; + + /** + * An optional output file for listing the unused classes and class + * members. An empty file name means the standard output. + */ + public File printUsage; + + /** + * A list of {@link ClassSpecification} instances, for which an explanation + * is to be printed, why they are kept in the shrinking step. + */ + public List whyAreYouKeeping; + + /////////////////////////////////////////////////////////////////////////// + // Optimization options. + /////////////////////////////////////////////////////////////////////////// + + /** + * Specifies whether the code should be optimized. + */ + public boolean optimize = true; + + /** + * A list of Strings specifying the optimizations to be + * performed. A null list means all optimizations. The + * optimization names may contain "*" or "?" wildcards, and they may + * be preceded by the "!" negator. + */ + public List optimizations; + + /** + * Specifies the number of optimization passes. + */ + public int optimizationPasses = 1; + + /** + * A list of {@link ClassSpecification} instances, whose methods are + * assumed to have no side effects. + */ + public List assumeNoSideEffects; + + /** + * Specifies whether the access of class members can be modified. + */ + public boolean allowAccessModification = false; + + /** + * Specifies whether interfaces may be merged aggressively. + */ + public boolean mergeInterfacesAggressively = false; + + /////////////////////////////////////////////////////////////////////////// + // Obfuscation options. + /////////////////////////////////////////////////////////////////////////// + + /** + * Specifies whether the code should be obfuscated. + */ + public boolean obfuscate = true; + + /** + * An optional output file for listing the obfuscation mapping. + * An empty file name means the standard output. + */ + public File printMapping; + + /** + * An optional input file for reading an obfuscation mapping. + */ + public File applyMapping; + + /** + * An optional name of a file containing obfuscated class member names. + */ + public File obfuscationDictionary; + + /** + * An optional name of a file containing obfuscated class names. + */ + public File classObfuscationDictionary; + + /** + * An optional name of a file containing obfuscated package names. + */ + public File packageObfuscationDictionary; + + /** + * Specifies whether to apply aggressive name overloading on class members. + */ + public boolean overloadAggressively = false; + + /** + * Specifies whether to generate globally unique class member names. + */ + public boolean useUniqueClassMemberNames = false; + + /** + * Specifies whether obfuscated packages and classes can get mixed-case names. + */ + public boolean useMixedCaseClassNames = true; + + /** + * A list of Strings specifying package names to be kept. + * A null list means no names. An empty list means all + * names. The package names may contain "**", "*", or "?" wildcards, and + * they may be preceded by the "!" negator. + */ + public List keepPackageNames; + + /** + * An optional base package if the obfuscated package hierarchy is to be + * flattened, null otherwise. + */ + public String flattenPackageHierarchy; + + /** + * An optional base package if the obfuscated classes are to be repackaged + * into a single package, null otherwise. + */ + public String repackageClasses; + + /** + * A list of Strings specifying optional attributes to be kept. + * A null list means no attributes. An empty list means all + * attributes. The attribute names may contain "*" or "?" wildcards, and + * they may be preceded by the "!" negator. + */ + public List keepAttributes; + + /** + * Specifies whether method parameter names and types should be kept for + * methods that are not obfuscated. This is achieved by keeping partial + * "LocalVariableTable" and "LocalVariableTypeTable" attributes. + */ + public boolean keepParameterNames = false; + + /** + * An optional replacement for all SourceFile attributes. + */ + public String newSourceFileAttribute; + + /** + * A list of Strings specifying a filter for classes whose + * string constants are to be adapted, based on corresponding obfuscated + * class names. + */ + public List adaptClassStrings; + + /** + * A list of Strings specifying a filter for files whose + * names are to be adapted, based on corresponding obfuscated class names. + */ + public List adaptResourceFileNames; + + /** + * A list of Strings 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. + /////////////////////////////////////////////////////////////////////////// + + /** + * Specifies whether to print verbose messages. + */ + public boolean verbose = false; + + /** + * A list of Strings specifying a filter for the classes for + * which not to print notes, if there are noteworthy potential problems. + * A null list means all classes. The class names may contain + * "**", "*", or "?" wildcards, and they may be preceded by the "!" negator. + */ + public List note = null; + + /** + * A list of Strings specifying a filter for the classes for + * which not to print warnings, if there are any problems. + * A null list means all classes. The class names may contain + * "**", "*", or "?" wildcards, and they may be preceded by the "!" negator. + */ + public List warn = null; + + /** + * Specifies whether to ignore any warnings. + */ + 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 + * or less readable form. An empty file name means the standard output. + */ + public File dump; +} diff --git a/src/proguard/ConfigurationConstants.java b/src/proguard/ConfigurationConstants.java new file mode 100644 index 000000000..14c565475 --- /dev/null +++ b/src/proguard/ConfigurationConstants.java @@ -0,0 +1,123 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 provides constants for parsing and writing ProGuard configurations. + * + * @author Eric Lafortune + */ +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 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 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 OPTIMIZATIONS = "-optimizations"; + public static final String OPTIMIZATION_PASSES = "-optimizationpasses"; + public static final String ASSUME_NO_SIDE_EFFECTS_OPTION = "-assumenosideeffects"; + public static final String ALLOW_ACCESS_MODIFICATION_OPTION = "-allowaccessmodification"; + public static final String MERGE_INTERFACES_AGGRESSIVELY_OPTION = "-mergeinterfacesaggressively"; + + 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 CLASS_OBFUSCATION_DICTIONARY_OPTION = "-classobfuscationdictionary"; + public static final String PACKAGE_OBFUSCATION_DICTIONARY_OPTION = "-packageobfuscationdictionary"; + public static final String OVERLOAD_AGGRESSIVELY_OPTION = "-overloadaggressively"; + public static final String USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION = "-useuniqueclassmembernames"; + public static final String DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION = "-dontusemixedcaseclassnames"; + public static final String KEEP_PACKAGE_NAMES_OPTION = "-keeppackagenames"; + public static final String FLATTEN_PACKAGE_HIERARCHY_OPTION = "-flattenpackagehierarchy"; + public static final String REPACKAGE_CLASSES_OPTION = "-repackageclasses"; + public static final String DEFAULT_PACKAGE_OPTION = "-defaultpackage"; + public static final String KEEP_ATTRIBUTES_OPTION = "-keepattributes"; + public static final String KEEP_PARAMETER_NAMES_OPTION = "-keepparameternames"; + public static final String RENAME_SOURCE_FILE_ATTRIBUTE_OPTION = "-renamesourcefileattribute"; + public static final String ADAPT_CLASS_STRINGS_OPTION = "-adaptclassstrings"; + public static final String ADAPT_RESOURCE_FILE_NAMES_OPTION = "-adaptresourcefilenames"; + public static final String ADAPT_RESOURCE_FILE_CONTENTS_OPTION = "-adaptresourcefilecontents"; + + 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 SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION = "-skipnonpubliclibraryclasses"; + public static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION = "-dontskipnonpubliclibraryclasses"; + public static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION = "-dontskipnonpubliclibraryclassmembers"; + public static final String TARGET_OPTION = "-target"; + public static final String KEEP_DIRECTORIES_OPTION = "-keepdirectories"; + public static final String FORCE_PROCESSING_OPTION = "-forceprocessing"; + + public static final String ANY_ATTRIBUTE_KEYWORD = "*"; + 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 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 = ""; + public static final String ANY_METHOD_KEYWORD = ""; + public static final String OPEN_ARGUMENTS_KEYWORD = "("; + public static final String ARGUMENT_SEPARATOR_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 new file mode 100644 index 000000000..864c82e66 --- /dev/null +++ b/src/proguard/ConfigurationParser.java @@ -0,0 +1,1347 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; +import proguard.classfile.util.ClassUtil; +import proguard.util.ListUtil; + +import java.io.*; +import java.net.URL; +import java.util.*; + + +/** + * This class parses ProGuard configurations. Configurations can be read from an + * array of arguments or from a configuration file or URL. External references + * in file names ('<...>') can be resolved against a given set of properties. + * + * @author Eric Lafortune + */ +public class ConfigurationParser +{ + private final WordReader reader; + private final Properties properties; + + private String nextWord; + private String lastComments; + + + /** + * Creates a new ConfigurationParser for the given String arguments and + * the given Properties. + */ + public ConfigurationParser(String[] args, + Properties properties) throws IOException + { + this(args, null, properties); + } + + + /** + * Creates a new ConfigurationParser for the given String arguments, + * with the given base directory and the given Properties. + */ + public ConfigurationParser(String[] args, + File baseDir, + Properties properties) throws IOException + { + this(new ArgumentWordReader(args, baseDir), properties); + } + + + /** + * Creates a new ConfigurationParser for the given lines, + * with the given base directory and the given Properties. + */ + public ConfigurationParser(String lines, + String description, + File baseDir, + Properties properties) throws IOException + { + this(new LineWordReader(new LineNumberReader(new StringReader(lines)), + description, + baseDir), + properties); + } + + + /** + * Creates a new ConfigurationParser for the given file, with the system + * Properties. + * @deprecated Temporary code for backward compatibility in Obclipse. + */ + public ConfigurationParser(File file) throws IOException + { + this(file, System.getProperties()); + } + + + /** + * Creates a new ConfigurationParser for the given file and the given + * Properties. + */ + public ConfigurationParser(File file, + Properties properties) throws IOException + { + this(new FileWordReader(file), properties); + } + + + /** + * Creates a new ConfigurationParser for the given URL and the given + * Properties. + */ + public ConfigurationParser(URL url, + Properties properties) throws IOException + { + this(new FileWordReader(url), properties); + } + + + /** + * Creates a new ConfigurationParser for the given word reader and the + * given Properties. + */ + public ConfigurationParser(WordReader reader, + Properties properties) throws IOException + { + this.reader = reader; + this.properties = properties; + + readNextWord(); + } + + + /** + * Parses and returns the configuration. + * @param configuration the configuration that is updated as a side-effect. + * @throws ParseException if the any of the configuration settings contains + * a syntax error. + * @throws IOException if an IO error occurs while reading a configuration. + */ + public void parse(Configuration configuration) + throws ParseException, IOException + { + while (nextWord != null) + { + lastComments = reader.lastComments(); + + // First include directives. + if (ConfigurationConstants.AT_DIRECTIVE .startsWith(nextWord) || + 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. + else if (ConfigurationConstants.INJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, false); + else if (ConfigurationConstants.OUTJARS_OPTION .startsWith(nextWord)) configuration.programJars = parseClassPathArgument(configuration.programJars, true); + else if (ConfigurationConstants.LIBRARYJARS_OPTION .startsWith(nextWord)) configuration.libraryJars = parseClassPathArgument(configuration.libraryJars, false); + 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.SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION .startsWith(nextWord)) configuration.skipNonPublicLibraryClasses = parseNoArgument(true); + 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.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 = parseKeepClassSpecificationArguments(configuration.keep, true, false, false); + else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, false, false); + else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, false); + else if (ConfigurationConstants.KEEP_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, true, false, true); + else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, false, true); + else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, true); + else if (ConfigurationConstants.PRINT_SEEDS_OPTION .startsWith(nextWord)) configuration.printSeeds = parseOptionalFile(); + + // After '-keep'. + else if (ConfigurationConstants.KEEP_DIRECTORIES_OPTION .startsWith(nextWord)) configuration.keepDirectories = parseCommaSeparatedList("directory name", true, true, false, true, false, true, false, false, configuration.keepDirectories); + + else if (ConfigurationConstants.DONT_SHRINK_OPTION .startsWith(nextWord)) configuration.shrink = parseNoArgument(false); + else if (ConfigurationConstants.PRINT_USAGE_OPTION .startsWith(nextWord)) configuration.printUsage = parseOptionalFile(); + else if (ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION .startsWith(nextWord)) configuration.whyAreYouKeeping = parseClassSpecificationArguments(configuration.whyAreYouKeeping); + + else if (ConfigurationConstants.DONT_OPTIMIZE_OPTION .startsWith(nextWord)) configuration.optimize = parseNoArgument(false); + else if (ConfigurationConstants.OPTIMIZATION_PASSES .startsWith(nextWord)) configuration.optimizationPasses = parseIntegerArgument(); + else if (ConfigurationConstants.OPTIMIZATIONS .startsWith(nextWord)) configuration.optimizations = parseCommaSeparatedList("optimization name", true, false, false, false, false, false, false, false, configuration.optimizations); + else if (ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION .startsWith(nextWord)) configuration.assumeNoSideEffects = parseClassSpecificationArguments(configuration.assumeNoSideEffects); + else if (ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION .startsWith(nextWord)) configuration.allowAccessModification = parseNoArgument(true); + else if (ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION .startsWith(nextWord)) configuration.mergeInterfacesAggressively = parseNoArgument(true); + + else if (ConfigurationConstants.DONT_OBFUSCATE_OPTION .startsWith(nextWord)) configuration.obfuscate = parseNoArgument(false); + else if (ConfigurationConstants.PRINT_MAPPING_OPTION .startsWith(nextWord)) configuration.printMapping = parseOptionalFile(); + else if (ConfigurationConstants.APPLY_MAPPING_OPTION .startsWith(nextWord)) configuration.applyMapping = parseFile(); + else if (ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.obfuscationDictionary = parseFile(); + else if (ConfigurationConstants.CLASS_OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.classObfuscationDictionary = parseFile(); + else if (ConfigurationConstants.PACKAGE_OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.packageObfuscationDictionary = 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.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION .startsWith(nextWord)) configuration.useMixedCaseClassNames = parseNoArgument(false); + else if (ConfigurationConstants.KEEP_PACKAGE_NAMES_OPTION .startsWith(nextWord)) configuration.keepPackageNames = parseCommaSeparatedList("package name", true, true, false, false, true, false, true, false, configuration.keepPackageNames); + else if (ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION .startsWith(nextWord)) configuration.flattenPackageHierarchy = ClassUtil.internalClassName(parseOptionalArgument()); + else if (ConfigurationConstants.REPACKAGE_CLASSES_OPTION .startsWith(nextWord)) configuration.repackageClasses = ClassUtil.internalClassName(parseOptionalArgument()); + else if (ConfigurationConstants.DEFAULT_PACKAGE_OPTION .startsWith(nextWord)) configuration.repackageClasses = ClassUtil.internalClassName(parseOptionalArgument()); + else if (ConfigurationConstants.KEEP_ATTRIBUTES_OPTION .startsWith(nextWord)) configuration.keepAttributes = parseCommaSeparatedList("attribute name", true, true, false, false, true, false, false, false, configuration.keepAttributes); + else if (ConfigurationConstants.KEEP_PARAMETER_NAMES_OPTION .startsWith(nextWord)) configuration.keepParameterNames = parseNoArgument(true); + else if (ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION .startsWith(nextWord)) configuration.newSourceFileAttribute = parseOptionalArgument(); + else if (ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION .startsWith(nextWord)) configuration.adaptClassStrings = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.adaptClassStrings); + else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION .startsWith(nextWord)) configuration.adaptResourceFileNames = parseCommaSeparatedList("resource file name", true, true, false, true, false, false, false, false, configuration.adaptResourceFileNames); + else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION .startsWith(nextWord)) configuration.adaptResourceFileContents = parseCommaSeparatedList("resource file name", true, true, false, true, false, false, false, false, configuration.adaptResourceFileContents); + + else if (ConfigurationConstants.DONT_PREVERIFY_OPTION .startsWith(nextWord)) configuration.preverify = parseNoArgument(false); + else if (ConfigurationConstants.MICRO_EDITION_OPTION .startsWith(nextWord)) configuration.microEdition = parseNoArgument(true); + + else if (ConfigurationConstants.VERBOSE_OPTION .startsWith(nextWord)) configuration.verbose = parseNoArgument(true); + else if (ConfigurationConstants.DONT_NOTE_OPTION .startsWith(nextWord)) configuration.note = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.note); + else if (ConfigurationConstants.DONT_WARN_OPTION .startsWith(nextWord)) configuration.warn = parseCommaSeparatedList("class name", true, true, false, false, true, false, true, false, configuration.warn); + else if (ConfigurationConstants.IGNORE_WARNINGS_OPTION .startsWith(nextWord)) configuration.ignoreWarnings = parseNoArgument(true); + else if (ConfigurationConstants.PRINT_CONFIGURATION_OPTION .startsWith(nextWord)) configuration.printConfiguration = parseOptionalFile(); + else if (ConfigurationConstants.DUMP_OPTION .startsWith(nextWord)) configuration.dump = parseOptionalFile(); + else + { + throw new ParseException("Unknown option " + reader.locationDescription()); + } + } + } + + + + /** + * Closes the configuration. + * @throws IOException if an IO error occurs while closing the configuration. + */ + public void close() throws IOException + { + if (reader != null) + { + reader.close(); + } + } + + + private long parseIncludeArgument(long lastModified) throws ParseException, IOException + { + // Read the configuation file name. + readNextWord("configuration file name", true, false); + + File file = file(nextWord); + reader.includeWordReader(new FileWordReader(file)); + + readNextWord(); + + return Math.max(lastModified, file.lastModified()); + } + + + private void parseBaseDirectoryArgument() throws ParseException, IOException + { + // Read the base directory name. + readNextWord("base directory name", true, false); + + reader.setBaseDir(file(nextWord)); + + readNextWord(); + } + + + private ClassPath parseClassPathArgument(ClassPath classPath, + boolean isOutput) + throws ParseException, IOException + { + // Create a new List if necessary. + if (classPath == null) + { + classPath = new ClassPath(); + } + + while (true) + { + // Read the next jar name. + readNextWord("jar or directory name", true, false); + + // Create a new class path entry. + ClassPathEntry entry = new ClassPathEntry(file(nextWord), isOutput); + + // Read the opening parenthesis or the separator, if any. + readNextWord(); + + // Read the optional filters. + if (!configurationEnd() && + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord)) + { + // Read all filters in an array. + List[] filters = new List[5]; + + int counter = 0; + do + { + // Read the filter. + filters[counter++] = + parseCommaSeparatedList("filter", true, true, true, true, false, true, false, false, null); + } + while (counter < filters.length && + ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)); + + // Make sure there is a closing parenthesis. + if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord)) + { + throw new ParseException("Expecting separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + + "' or '" + ConfigurationConstants.SEPARATOR_KEYWORD + + "', or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + + "' before " + reader.locationDescription()); + } + + // Set all filters from the array on the entry. + entry.setFilter(filters[--counter]); + if (counter > 0) + { + entry.setJarFilter(filters[--counter]); + if (counter > 0) + { + entry.setWarFilter(filters[--counter]); + if (counter > 0) + { + entry.setEarFilter(filters[--counter]); + if (counter > 0) + { + entry.setZipFilter(filters[--counter]); + } + } + } + } + + // Read the separator, if any. + readNextWord(); + } + + // Add the entry to the list. + classPath.add(entry); + + if (configurationEnd()) + { + return classPath; + } + + if (!nextWord.equals(ConfigurationConstants.JAR_SEPARATOR_KEYWORD)) + { + throw new ParseException("Expecting class path separator '" + ConfigurationConstants.JAR_SEPARATOR_KEYWORD + + "' before " + reader.locationDescription()); + } + } + } + + + private int parseClassVersion() + throws ParseException, IOException + { + // Read the obligatory target. + readNextWord("java version"); + + int classVersion = ClassUtil.internalClassVersion(nextWord); + if (classVersion == 0) + { + throw new ParseException("Unsupported java version " + reader.locationDescription()); + } + + readNextWord(); + + return classVersion; + } + + + private int parseIntegerArgument() + throws ParseException, IOException + { + try + { + // Read the obligatory integer. + readNextWord("integer"); + + int integer = Integer.parseInt(nextWord); + + readNextWord(); + + return integer; + } + catch (NumberFormatException e) + { + throw new ParseException("Expecting integer argument instead of '" + nextWord + + "' before " + reader.locationDescription()); + } + } + + + private File parseFile() + throws ParseException, IOException + { + // Read the obligatory file name. + readNextWord("file name", true, false); + + // Make sure the file is properly resolved. + File file = file(nextWord); + + readNextWord(); + + return file; + } + + + private File parseOptionalFile() + throws ParseException, IOException + { + // Read the optional file name. + readNextWord(true); + + // Didn't the user specify a file name? + if (configurationEnd()) + { + return Configuration.STD_OUT; + } + + // Make sure the file is properly resolved. + File file = file(nextWord); + + readNextWord(); + + return file; + } + + + private String parseOptionalArgument() throws IOException + { + // Read the optional argument. + readNextWord(); + + // Didn't the user specify an argument? + if (configurationEnd()) + { + return ""; + } + + String argument = nextWord; + + readNextWord(); + + return argument; + } + + + private boolean parseNoArgument(boolean value) throws IOException + { + readNextWord(); + + return value; + } + + + private long parseNoArgument(long value) throws IOException + { + readNextWord(); + + return value; + } + + + private List parseKeepClassSpecificationArguments(List keepClassSpecifications, + boolean markClasses, + boolean markConditionally, + boolean allowShrinking) + throws ParseException, IOException + { + // Create a new List if necessary. + if (keepClassSpecifications == null) + { + keepClassSpecifications = new ArrayList(); + } + + //boolean allowShrinking = false; + boolean allowOptimization = false; + boolean allowObfuscation = false; + + // Read the keep modifiers. + while (true) + { + readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + + "', '" + ClassConstants.EXTERNAL_ACC_INTERFACE + + "', or '" + ClassConstants.EXTERNAL_ACC_ENUM + "'", + false, 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. + keepClassSpecifications.add(new KeepClassSpecification(markClasses, + markConditionally, + allowShrinking, + allowOptimization, + allowObfuscation, + classSpecification)); + return keepClassSpecifications; + } + + + private List parseClassSpecificationArguments(List classSpecifications) + throws ParseException, IOException + { + // Create a new List if necessary. + if (classSpecifications == null) + { + classSpecifications = new ArrayList(); + } + + // Read and add the class configuration. + readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + + "', '" + ClassConstants.EXTERNAL_ACC_INTERFACE + + "', or '" + ClassConstants.EXTERNAL_ACC_ENUM + "'", + false, true); + + classSpecifications.add(parseClassSpecificationArguments()); + + return classSpecifications; + } + + + /** + * Parses and returns a class specification. + * @throws ParseException if the class specification contains a syntax error. + * @throws IOException if an IO error occurs while reading the class + * specification. + */ + public ClassSpecification parseClassSpecificationArguments() + throws ParseException, IOException + { + // Clear the annotation type. + String annotationType = null; + + // Clear the class access modifiers. + int requiredSetClassAccessFlags = 0; + int requiredUnsetClassAccessFlags = 0; + + // Parse the class annotations and access modifiers until the class keyword. + while (!ConfigurationConstants.CLASS_KEYWORD.equals(nextWord)) + { + // Strip the negating sign, if any. + boolean negated = + nextWord.startsWith(ConfigurationConstants.NEGATOR_KEYWORD); + + String strippedWord = negated ? + 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 : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC) ? ClassConstants.INTERNAL_ACC_SYNTHETIC : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_ANNOTATION) ? ClassConstants.INTERNAL_ACC_ANNOTATTION : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_ENUM) ? ClassConstants.INTERNAL_ACC_ENUM : + unknownAccessFlag(); + + // Is it an annotation modifier? + if (accessFlag == ClassConstants.INTERNAL_ACC_ANNOTATTION) + { + // Already read the next word. + readNextWord("annotation type or keyword '" + ClassConstants.EXTERNAL_ACC_INTERFACE + "'", + false, false); + + // Is the next word actually an annotation type? + if (!nextWord.equals(ClassConstants.EXTERNAL_ACC_INTERFACE) && + !nextWord.equals(ClassConstants.EXTERNAL_ACC_ENUM) && + !nextWord.equals(ConfigurationConstants.CLASS_KEYWORD)) + { + // Parse the annotation type. + annotationType = + ListUtil.commaSeparatedString( + parseCommaSeparatedList("annotation type", + false, false, false, false, true, false, false, true, null), false); + + // Continue parsing the access modifier that we just read + // in the next cycle. + continue; + } + + // Otherwise just handle the annotation modifier. + } + + if (!negated) + { + requiredSetClassAccessFlags |= accessFlag; + } + else + { + requiredUnsetClassAccessFlags |= accessFlag; + } + + if ((requiredSetClassAccessFlags & + requiredUnsetClassAccessFlags) != 0) + { + throw new ParseException("Conflicting class access modifiers for '" + strippedWord + + "' before " + reader.locationDescription()); + } + + if (strippedWord.equals(ClassConstants.EXTERNAL_ACC_INTERFACE) || + strippedWord.equals(ClassConstants.EXTERNAL_ACC_ENUM) || + strippedWord.equals(ConfigurationConstants.CLASS_KEYWORD)) + { + // The interface or enum keyword. Stop parsing the class flags. + break; + } + + // Should we read the next word? + if (accessFlag != ClassConstants.INTERNAL_ACC_ANNOTATTION) + { + readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + + "', '" + ClassConstants.EXTERNAL_ACC_INTERFACE + + "', or '" + ClassConstants.EXTERNAL_ACC_ENUM + "'", + false, true); + } + } + + // Parse the class name part. + String externalClassName = + ListUtil.commaSeparatedString( + parseCommaSeparatedList("class name or interface name", + true, false, false, false, true, false, false, false, null), false); + + // For backward compatibility, allow a single "*" wildcard to match any + // class. + String className = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalClassName) ? + null : + ClassUtil.internalClassName(externalClassName); + + // Clear the annotation type and the class name of the extends part. + String extendsAnnotationType = null; + String extendsClassName = null; + + if (!configurationEnd()) + { + // Parse 'implements ...' or 'extends ...' part, if any. + if (ConfigurationConstants.IMPLEMENTS_KEYWORD.equals(nextWord) || + ConfigurationConstants.EXTENDS_KEYWORD.equals(nextWord)) + { + readNextWord("class name or interface name", false, true); + + // Parse the annotation type, if any. + if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord)) + { + extendsAnnotationType = + ListUtil.commaSeparatedString( + parseCommaSeparatedList("annotation type", + true, false, false, false, true, false, false, true, null), false); + } + + String externalExtendsClassName = + ListUtil.commaSeparatedString( + parseCommaSeparatedList("class name or interface name", + false, false, false, false, true, false, false, false, null), false); + + extendsClassName = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalExtendsClassName) ? + null : + ClassUtil.internalClassName(externalExtendsClassName); + } + } + + // Create the basic class specification. + ClassSpecification classSpecification = + new ClassSpecification(lastComments, + requiredSetClassAccessFlags, + requiredUnsetClassAccessFlags, + annotationType, + className, + extendsAnnotationType, + extendsClassName); + + + // Now add any class members to this class specification. + if (!configurationEnd()) + { + // Check the class member opening part. + if (!ConfigurationConstants.OPEN_KEYWORD.equals(nextWord)) + { + throw new ParseException("Expecting opening '" + ConfigurationConstants.OPEN_KEYWORD + + "' at " + reader.locationDescription()); + } + + // Parse all class members. + while (true) + { + readNextWord("class member description" + + " or closing '" + ConfigurationConstants.CLOSE_KEYWORD + "'", + false, true); + + if (nextWord.equals(ConfigurationConstants.CLOSE_KEYWORD)) + { + // The closing brace. Stop parsing the class members. + readNextWord(); + + break; + } + + parseMemberSpecificationArguments(externalClassName, + classSpecification); + } + } + + return 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 (!configurationEnd(true)) + { + // Parse the annotation type, if any. + if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord)) + { + annotationType = + ListUtil.commaSeparatedString( + parseCommaSeparatedList("annotation type", + true, false, false, false, true, false, false, true, null), 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 : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_PROTECTED) ? ClassConstants.INTERNAL_ACC_PROTECTED : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_STATIC) ? ClassConstants.INTERNAL_ACC_STATIC : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED) ? ClassConstants.INTERNAL_ACC_SYNCHRONIZED : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_VOLATILE) ? ClassConstants.INTERNAL_ACC_VOLATILE : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_TRANSIENT) ? ClassConstants.INTERNAL_ACC_TRANSIENT : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_BRIDGE) ? ClassConstants.INTERNAL_ACC_BRIDGE : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_VARARGS) ? ClassConstants.INTERNAL_ACC_VARARGS : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_NATIVE) ? ClassConstants.INTERNAL_ACC_NATIVE : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_STRICT) ? ClassConstants.INTERNAL_ACC_STRICT : + strippedWord.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC) ? ClassConstants.INTERNAL_ACC_SYNTHETIC : + 0; + if (accessFlag == 0) + { + // Not a class member access modifier. Stop parsing them. + break; + } + + if (strippedWord.equals(nextWord)) + { + requiredSetMemberAccessFlags |= accessFlag; + } + else + { + requiredUnsetMemberAccessFlags |= accessFlag; + } + + // Make sure the user doesn't try to set and unset the same + // access flags simultaneously. + if ((requiredSetMemberAccessFlags & + requiredUnsetMemberAccessFlags) != 0) + { + throw new ParseException("Conflicting class member access modifiers for " + + reader.locationDescription()); + } + + readNextWord("class member description"); + } + + // Parse the class member type and name part. + + // Did we get a special wildcard? + if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord) || + ConfigurationConstants.ANY_FIELD_KEYWORD .equals(nextWord) || + ConfigurationConstants.ANY_METHOD_KEYWORD .equals(nextWord)) + { + // Act according to the type of wildcard.. + if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord)) + { + checkFieldAccessFlags(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags); + checkMethodAccessFlags(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags); + + classSpecification.addField( + new MemberSpecification(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags, + annotationType, + null, + null)); + classSpecification.addMethod( + new MemberSpecification(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags, + annotationType, + null, + null)); + } + else if (ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord)) + { + checkFieldAccessFlags(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags); + + classSpecification.addField( + new MemberSpecification(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags, + annotationType, + null, + null)); + } + else if (ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord)) + { + checkMethodAccessFlags(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags); + + classSpecification.addMethod( + new MemberSpecification(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags, + annotationType, + null, + null)); + } + + // We still have to read the closing separator. + readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); + + if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) + { + throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + + "' before " + reader.locationDescription()); + } + } + else + { + // Make sure we have a proper type. + checkJavaIdentifier("java type"); + String type = nextWord; + + readNextWord("class member name"); + String name = nextWord; + + // Did we get just one word before the opening parenthesis? + if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(name)) + { + // This must be a constructor then. + // Make sure the type is a proper constructor name. + if (!(type.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) || + type.equals(externalClassName) || + type.equals(ClassUtil.externalShortClassName(externalClassName)))) + { + throw new ParseException("Expecting type and name " + + "instead of just '" + type + + "' before " + reader.locationDescription()); + } + + // Assign the fixed constructor type and name. + type = ClassConstants.EXTERNAL_TYPE_VOID; + name = ClassConstants.INTERNAL_METHOD_NAME_INIT; + } + else + { + // It's not a constructor. + // Make sure we have a proper name. + checkJavaIdentifier("class member name"); + + // Read the opening parenthesis or the separating + // semi-colon. + readNextWord("opening '" + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + + "' or separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); + } + + // Are we looking at a field, a method, or something else? + if (ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) + { + // It's a field. + checkFieldAccessFlags(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags); + + // We already have a field descriptor. + String descriptor = ClassUtil.internalType(type); + + // Add the field. + classSpecification.addField( + new MemberSpecification(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags, + annotationType, + name, + descriptor)); + } + else if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord)) + { + // It's a method. + checkMethodAccessFlags(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags); + + // Parse the method arguments. + String descriptor = + ClassUtil.internalMethodDescriptor(type, + parseCommaSeparatedList("argument", true, true, true, false, true, false, false, false, null)); + + if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord)) + { + throw new ParseException("Expecting separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + + "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + + "' before " + reader.locationDescription()); + } + + // Read the separator after the closing parenthesis. + readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'"); + + if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)) + { + throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + + "' before " + reader.locationDescription()); + } + + // Add the method. + classSpecification.addMethod( + new MemberSpecification(requiredSetMemberAccessFlags, + requiredUnsetMemberAccessFlags, + annotationType, + name, + descriptor)); + } + else + { + // It doesn't look like a field or a method. + throw new ParseException("Expecting opening '" + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + + "' or separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + + "' before " + reader.locationDescription()); + } + } + } + + + /** + * Reads a comma-separated list of java identifiers or of file names. + * Examples of invocation arguments: + * ("directory n", true, true, false, true, false, true, false, false, ...) + * ("optimizatio", true, false, false, false, false, false, false, false, ...) + * ("package nam", true, true, false, false, true, false, true, false, ...) + * ("attribute n", true, true, false, false, true, false, false, false, ...) + * ("class name", true, true, false, false, true, false, true, false, ...) + * ("resource fi", true, true, false, true, false, false, false, false, ...) + * ("resource fi", true, true, false, true, false, false, false, false, ...) + * ("class name", true, true, false, false, true, false, true, false, ...) + * ("class name", true, true, false, false, true, false, true, false, ...) + * ("filter", true, true, true, true, false, true, false, false, ...) + * ("annotation ", false, false, false, false, true, false, false, true, ...) + * ("class name ", true, false, false, false, true, false, false, false, ...) + * ("annotation ", true, false, false, false, true, false, false, true, ...) + * ("class name ", false, false, false, false, true, false, false, false, ...) + * ("annotation ", true, false, false, false, true, false, false, true, ...) + * ("argument", true, true, true, false, true, false, false, false, ...) + */ + private List parseCommaSeparatedList(String expectedDescription, + boolean readFirstWord, + boolean allowEmptyList, + boolean expectClosingParenthesis, + boolean isFileName, + boolean checkJavaIdentifiers, + boolean replaceSystemProperties, + boolean replaceExternalClassNames, + boolean replaceExternalTypes, + List list) + throws ParseException, IOException + { + if (list == null) + { + list = new ArrayList(); + } + + if (readFirstWord) + { + if (!allowEmptyList) + { + // Read the first list entry. + readNextWord(expectedDescription, isFileName, false); + } + else if (expectClosingParenthesis) + { + // Read the first list entry. + readNextWord(expectedDescription, isFileName, false); + + // Return if the entry is actually empty (an empty file name or + // a closing parenthesis). + if (nextWord.length() == 0) + { + // Read the closing parenthesis + readNextWord("closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + + "'"); + + return list; + } + else if (nextWord.equals(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD)) + { + return list; + } + } + else + { + // Read the first list entry, if there is any. + readNextWord(isFileName); + + // Check if the list is empty. + if (configurationEnd()) + { + return list; + } + } + } + + while (true) + { + if (checkJavaIdentifiers) + { + checkJavaIdentifier("java type"); + } + + if (replaceSystemProperties) + { + nextWord = replaceSystemProperties(nextWord); + } + + if (replaceExternalClassNames) + { + nextWord = ClassUtil.internalClassName(nextWord); + } + + if (replaceExternalTypes) + { + nextWord = ClassUtil.internalType(nextWord); + } + + list.add(nextWord); + + if (expectClosingParenthesis) + { + // Read a comma (or a closing parenthesis, or a different word). + readNextWord("separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + + "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD + + "'"); + } + else + { + // Read a comma (or a different word). + readNextWord(); + } + + if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord)) + { + return list; + } + + // Read the next list entry. + readNextWord(expectedDescription, isFileName, false); + } + } + + + /** + * Throws a ParseException for an unexpected keyword. + */ + private int unknownAccessFlag() throws ParseException + { + throw new ParseException("Unexpected keyword " + reader.locationDescription()); + } + + + /** + * Creates a properly resolved File, based on the given word. + */ + private File file(String word) throws ParseException + { + String fileName = replaceSystemProperties(word); + File file = new File(fileName); + + // Try to get an absolute file. + if (!file.isAbsolute()) + { + file = new File(reader.getBaseDir(), fileName); + } + + return file; + } + + + /** + * Replaces any properties in the given word by their values. + * For instance, the substring "" is replaced by its value. + */ + public String replaceSystemProperties(String word) throws ParseException + { + int fromIndex = 0; + while (true) + { + fromIndex = word.indexOf(ConfigurationConstants.OPEN_SYSTEM_PROPERTY, fromIndex); + if (fromIndex < 0) + { + break; + } + + int toIndex = word.indexOf(ConfigurationConstants.CLOSE_SYSTEM_PROPERTY, fromIndex+1); + if (toIndex < 0) + { + throw new ParseException("Expecting closing '" + ConfigurationConstants.CLOSE_SYSTEM_PROPERTY + + "' after opening '" + ConfigurationConstants.OPEN_SYSTEM_PROPERTY + + "' in " + reader.locationDescription()); + } + + String propertyName = word.substring(fromIndex+1, toIndex); + String propertyValue = properties.getProperty(propertyName); + if (propertyValue == null) + { + throw new ParseException("Value of system property '" + propertyName + + "' is undefined in " + reader.locationDescription()); + } + + word = word.substring(0, fromIndex) + + propertyValue + + word.substring(toIndex+1); + } + + return word; + } + + + /** + * 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) + throws ParseException, IOException + { + readNextWord(expectedDescription, false, 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 isFileName, + boolean expectingAtCharacter) + throws ParseException, IOException + { + readNextWord(isFileName); + if (configurationEnd(expectingAtCharacter)) + { + throw new ParseException("Expecting " + expectedDescription + + " before " + reader.locationDescription()); + } + } + + + /** + * Reads the next word of the configuration in the 'nextWord' field. + */ + private void readNextWord() throws IOException + { + readNextWord(false); + } + + + /** + * Reads the next word of the configuration in the 'nextWord' field. + */ + private void readNextWord(boolean isFileName) throws IOException + { + nextWord = reader.nextWord(isFileName); + } + + + /** + * Returns whether the end of the configuration has been reached. + */ + 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) || + (!expectingAtCharacter && + nextWord.equals(ConfigurationConstants.AT_DIRECTIVE)); + } + + + /** + * Checks whether the given word is a valid Java identifier and throws + * a ParseException if it isn't. Wildcard characters are accepted. + */ + private void checkJavaIdentifier(String expectedDescription) + throws ParseException + { + if (!isJavaIdentifier(nextWord)) + { + throw new ParseException("Expecting " + expectedDescription + + " before " + reader.locationDescription()); + } + } + + + /** + * Returns whether the given word is a valid Java identifier. + * Wildcard characters are accepted. + */ + private boolean isJavaIdentifier(String aWord) + { + if (aWord.length() == 0) + { + return false; + } + + for (int index = 0; index < aWord.length(); index++) + { + char c = aWord.charAt(index); + if (!(Character.isJavaIdentifierPart(c) || + c == '.' || + c == '[' || + c == ']' || + c == '<' || + c == '>' || + c == '-' || + c == '!' || + c == '*' || + c == '?' || + c == '%')) + { + return false; + } + } + + return true; + } + + + /** + * Checks whether the given access flags are valid field access flags, + * throwing a ParseException if they aren't. + */ + private void checkFieldAccessFlags(int requiredSetMemberAccessFlags, + int requiredUnsetMemberAccessFlags) + throws ParseException + { + if (((requiredSetMemberAccessFlags | + requiredUnsetMemberAccessFlags) & + ~ClassConstants.VALID_INTERNAL_ACC_FIELD) != 0) + { + throw new ParseException("Invalid method access modifier for field before " + + reader.locationDescription()); + } + } + + + /** + * Checks whether the given access flags are valid method access flags, + * throwing a ParseException if they aren't. + */ + private void checkMethodAccessFlags(int requiredSetMemberAccessFlags, + int requiredUnsetMemberAccessFlags) + throws ParseException + { + if (((requiredSetMemberAccessFlags | + requiredUnsetMemberAccessFlags) & + ~ClassConstants.VALID_INTERNAL_ACC_METHOD) != 0) + { + throw new ParseException("Invalid field access modifier for method before " + + reader.locationDescription()); + } + } + + + /** + * A main method for testing configuration parsing. + */ + public static void main(String[] args) + { + try + { + ConfigurationParser parser = + new ConfigurationParser(args, System.getProperties()); + + try + { + parser.parse(new Configuration()); + } + catch (ParseException ex) + { + ex.printStackTrace(); + } + finally + { + parser.close(); + } + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/ConfigurationWriter.java b/src/proguard/ConfigurationWriter.java new file mode 100644 index 000000000..00dfa9efd --- /dev/null +++ b/src/proguard/ConfigurationWriter.java @@ -0,0 +1,651 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; +import proguard.classfile.util.ClassUtil; +import proguard.util.ListUtil; + +import java.io.*; +import java.util.*; + + +/** + * This class writes ProGuard configurations to a file. + * + * @author Eric Lafortune + */ +public class ConfigurationWriter +{ + private static final String[] KEEP_OPTIONS = new String[] + { + ConfigurationConstants.KEEP_OPTION, + ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION, + ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION + }; + + + private final PrintWriter writer; + private File baseDir; + + + /** + * Creates a new ConfigurationWriter for the given file name. + */ + public ConfigurationWriter(File configurationFile) throws IOException + { + this(new PrintWriter(new FileWriter(configurationFile))); + + baseDir = configurationFile.getParentFile(); + } + + + /** + * Creates a new ConfigurationWriter for the given OutputStream. + */ + public ConfigurationWriter(OutputStream outputStream) throws IOException + { + this(new PrintWriter(outputStream)); + } + + + /** + * Creates a new ConfigurationWriter for the given PrintWriter. + */ + public ConfigurationWriter(PrintWriter writer) throws IOException + { + this.writer = writer; + } + + + /** + * Closes this ConfigurationWriter. + */ + public void close() throws IOException + { + writer.close(); + } + + + /** + * Writes the given configuration. + * @param configuration the configuration that is to be written out. + * @throws IOException if an IO error occurs while writing the configuration. + */ + public void write(Configuration configuration) throws IOException + { + // Write the program class path (input and output entries). + writeJarOptions(ConfigurationConstants.INJARS_OPTION, + ConfigurationConstants.OUTJARS_OPTION, + configuration.programJars); + writer.println(); + + // Write the library class path (output entries only). + writeJarOptions(ConfigurationConstants.LIBRARYJARS_OPTION, + ConfigurationConstants.LIBRARYJARS_OPTION, + configuration.libraryJars); + writer.println(); + + // Write the other options. + writeOption(ConfigurationConstants.SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION, configuration.skipNonPublicLibraryClasses); + writeOption(ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION, !configuration.skipNonPublicLibraryClassMembers); + writeOption(ConfigurationConstants.KEEP_DIRECTORIES_OPTION, configuration.keepDirectories); + writeOption(ConfigurationConstants.TARGET_OPTION, ClassUtil.externalClassVersion(configuration.targetClassVersion)); + writeOption(ConfigurationConstants.FORCE_PROCESSING_OPTION, configuration.lastModified == Long.MAX_VALUE); + + writeOption(ConfigurationConstants.DONT_SHRINK_OPTION, !configuration.shrink); + writeOption(ConfigurationConstants.PRINT_USAGE_OPTION, configuration.printUsage); + + writeOption(ConfigurationConstants.DONT_OPTIMIZE_OPTION, !configuration.optimize); + writeOption(ConfigurationConstants.OPTIMIZATIONS, configuration.optimizations); + writeOption(ConfigurationConstants.OPTIMIZATION_PASSES, configuration.optimizationPasses); + writeOption(ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION, configuration.allowAccessModification); + writeOption(ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION, configuration.mergeInterfacesAggressively); + + 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.CLASS_OBFUSCATION_DICTIONARY_OPTION, configuration.classObfuscationDictionary); + writeOption(ConfigurationConstants.PACKAGE_OBFUSCATION_DICTIONARY_OPTION, configuration.packageObfuscationDictionary); + 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.KEEP_PACKAGE_NAMES_OPTION, configuration.keepPackageNames, true); + writeOption(ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION, configuration.flattenPackageHierarchy, true); + writeOption(ConfigurationConstants.REPACKAGE_CLASSES_OPTION, configuration.repackageClasses, true); + writeOption(ConfigurationConstants.KEEP_ATTRIBUTES_OPTION, configuration.keepAttributes); + writeOption(ConfigurationConstants.KEEP_PARAMETER_NAMES_OPTION, configuration.keepParameterNames); + writeOption(ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION, configuration.newSourceFileAttribute); + writeOption(ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION, configuration.adaptClassStrings, true); + writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION, configuration.adaptResourceFileNames); + writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION, configuration.adaptResourceFileContents); + + writeOption(ConfigurationConstants.DONT_PREVERIFY_OPTION, !configuration.preverify); + writeOption(ConfigurationConstants.MICRO_EDITION_OPTION, configuration.microEdition); + + writeOption(ConfigurationConstants.VERBOSE_OPTION, configuration.verbose); + writeOption(ConfigurationConstants.DONT_NOTE_OPTION, configuration.note, true); + writeOption(ConfigurationConstants.DONT_WARN_OPTION, configuration.warn, true); + writeOption(ConfigurationConstants.IGNORE_WARNINGS_OPTION, configuration.ignoreWarnings); + writeOption(ConfigurationConstants.PRINT_CONFIGURATION_OPTION, configuration.printConfiguration); + writeOption(ConfigurationConstants.DUMP_OPTION, configuration.dump); + + writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION, configuration.printSeeds); + writer.println(); + + // Write the "why are you keeping" options. + writeOptions(ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION, configuration.whyAreYouKeeping); + + // Write the keep options. + writeOptions(KEEP_OPTIONS, configuration.keep); + + // Write the "no side effect methods" options. + writeOptions(ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION, configuration.assumeNoSideEffects); + + if (writer.checkError()) + { + throw new IOException("Can't write configuration"); + } + } + + + private void writeJarOptions(String inputEntryOptionName, + String outputEntryOptionName, + ClassPath classPath) + { + if (classPath != null) + { + for (int index = 0; index < classPath.size(); index++) + { + ClassPathEntry entry = classPath.get(index); + String optionName = entry.isOutput() ? + outputEntryOptionName : + inputEntryOptionName; + + writer.print(optionName); + writer.print(' '); + writer.print(relativeFileName(entry.getFile())); + + // Append the filters, if any. + boolean filtered = false; + + filtered = writeFilter(filtered, entry.getZipFilter()); + filtered = writeFilter(filtered, entry.getEarFilter()); + filtered = writeFilter(filtered, entry.getWarFilter()); + filtered = writeFilter(filtered, entry.getJarFilter()); + filtered = writeFilter(filtered, entry.getFilter()); + + if (filtered) + { + writer.print(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD); + } + + writer.println(); + } + } + } + + + private boolean writeFilter(boolean filtered, List filter) + { + if (filtered) + { + writer.print(ConfigurationConstants.SEPARATOR_KEYWORD); + } + + if (filter != null) + { + if (!filtered) + { + writer.print(ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD); + } + + writer.print(ListUtil.commaSeparatedString(filter, true)); + + filtered = true; + } + + return filtered; + } + + + private void writeOption(String optionName, boolean flag) + { + if (flag) + { + writer.println(optionName); + } + } + + + private void writeOption(String optionName, int argument) + { + if (argument != 1) + { + writer.print(optionName); + writer.print(' '); + writer.println(argument); + } + } + + + private void writeOption(String optionName, List arguments) + { + writeOption(optionName, arguments, false); + } + + + private void writeOption(String optionName, + List arguments, + boolean replaceInternalClassNames) + { + if (arguments != null) + { + if (arguments.isEmpty()) + { + writer.println(optionName); + } + else + { + if (replaceInternalClassNames) + { + arguments = externalClassNames(arguments); + } + + writer.print(optionName); + writer.print(' '); + writer.println(ListUtil.commaSeparatedString(arguments, true)); + } + } + } + + + private void writeOption(String optionName, String arguments) + { + writeOption(optionName, arguments, false); + } + + + private void writeOption(String optionName, + String arguments, + boolean replaceInternalClassNames) + { + if (arguments != null) + { + if (replaceInternalClassNames) + { + arguments = ClassUtil.externalClassName(arguments); + } + + writer.print(optionName); + writer.print(' '); + writer.println(quotedString(arguments)); + } + } + + + private void writeOption(String optionName, File file) + { + if (file != null) + { + if (file.getPath().length() > 0) + { + writer.print(optionName); + writer.print(' '); + writer.println(relativeFileName(file)); + } + else + { + writer.println(optionName); + } + } + } + + + private void writeOptions(String[] optionNames, + List keepClassSpecifications) + { + if (keepClassSpecifications != null) + { + for (int index = 0; index < keepClassSpecifications.size(); index++) + { + writeOption(optionNames, (KeepClassSpecification)keepClassSpecifications.get(index)); + } + } + } + + + private void writeOption(String[] optionNames, + KeepClassSpecification keepClassSpecification) + { + // Compose the option name. + String optionName = optionNames[keepClassSpecification.markConditionally ? 2 : + keepClassSpecification.markClasses ? 0 : + 1]; + + if (keepClassSpecification.allowShrinking) + { + optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + + ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION; + } + + if (keepClassSpecification.allowOptimization) + { + optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + + ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION; + } + + if (keepClassSpecification.allowObfuscation) + { + optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + + ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION; + } + + // Write out the option with the proper class specification. + writeOption(optionName, keepClassSpecification); + } + + + private void writeOptions(String optionName, + List classSpecifications) + { + if (classSpecifications != null) + { + for (int index = 0; index < classSpecifications.size(); index++) + { + writeOption(optionName, (ClassSpecification)classSpecifications.get(index)); + } + } + } + + + private void writeOption(String optionName, + ClassSpecification classSpecification) + { + writer.println(); + + // Write out the comments for this option. + writeComments(classSpecification.comments); + + writer.print(optionName); + 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, + ConfigurationConstants.NEGATOR_KEYWORD)); + + writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredSetAccessFlags)); + + // Write out the class keyword, if we didn't write the interface + // keyword earlier. + if (((classSpecification.requiredSetAccessFlags | + classSpecification.requiredUnsetAccessFlags) & + (ClassConstants.INTERNAL_ACC_INTERFACE | + ClassConstants.INTERNAL_ACC_ENUM)) == 0) + { + writer.print(ConfigurationConstants.CLASS_KEYWORD); + } + + writer.print(' '); + + // Write out the class name. + writer.print(classSpecification.className != null ? + ClassUtil.externalClassName(classSpecification.className) : + ConfigurationConstants.ANY_CLASS_KEYWORD); + + // Write out the extends template, if any. + if (classSpecification.extendsAnnotationType != null || + classSpecification.extendsClassName != null) + { + 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.print(' '); + writer.println(ConfigurationConstants.OPEN_KEYWORD); + + writeFieldSpecification( classSpecification.fieldSpecifications); + writeMethodSpecification(classSpecification.methodSpecifications); + + writer.println(ConfigurationConstants.CLOSE_KEYWORD); + } + else + { + writer.println(); + } + } + + + + private void writeComments(String comments) + { + if (comments != null) + { + int index = 0; + while (index < comments.length()) + { + int breakIndex = comments.indexOf('\n', index); + if (breakIndex < 0) + { + breakIndex = comments.length(); + } + + writer.print('#'); + + if (comments.charAt(index) != ' ') + { + writer.print(' '); + } + + writer.println(comments.substring(index, breakIndex)); + + index = breakIndex + 1; + } + } + } + + + private void writeFieldSpecification(List memberSpecifications) + { + if (memberSpecifications != null) + { + for (int index = 0; index < memberSpecifications.size(); 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(memberSpecification.requiredUnsetAccessFlags, + ConfigurationConstants.NEGATOR_KEYWORD)); + + writer.print(ClassUtil.externalFieldAccessFlags(memberSpecification.requiredSetAccessFlags)); + + // Write out the field name and descriptor. + String name = memberSpecification.name; + String descriptor = memberSpecification.descriptor; + + writer.print(descriptor == null ? name == null ? + ConfigurationConstants.ANY_FIELD_KEYWORD : + ConfigurationConstants.ANY_TYPE_KEYWORD + ' ' + name : + ClassUtil.externalFullFieldDescription(0, + name == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name, + descriptor)); + + writer.println(ConfigurationConstants.SEPARATOR_KEYWORD); + } + } + } + + + private void writeMethodSpecification(List memberSpecifications) + { + if (memberSpecifications != null) + { + for (int index = 0; index < memberSpecifications.size(); 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(memberSpecification.requiredUnsetAccessFlags, + ConfigurationConstants.NEGATOR_KEYWORD)); + + writer.print(ClassUtil.externalMethodAccessFlags(memberSpecification.requiredSetAccessFlags)); + + // Write out the method name and descriptor. + String name = memberSpecification.name; + String descriptor = memberSpecification.descriptor; + + 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 == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name, + descriptor)); + + writer.println(ConfigurationConstants.SEPARATOR_KEYWORD); + } + } + } + + + /** + * Returns a list with external versions of the given list of internal + * class names. + */ + private List externalClassNames(List internalClassNames) + { + List externalClassNames = new ArrayList(internalClassNames.size()); + + for (int index = 0; index < internalClassNames.size(); index++) + { + externalClassNames.add(ClassUtil.externalClassName((String)internalClassNames.get(index))); + } + + return externalClassNames; + } + + + /** + * Returns a relative file name of the given file, if possible. + * The file name is also quoted, if necessary. + */ + private String relativeFileName(File file) + { + String fileName = file.getAbsolutePath(); + + // See if we can convert the file name into a relative file name. + if (baseDir != null) + { + String baseDirName = baseDir.getAbsolutePath() + File.separator; + if (fileName.startsWith(baseDirName)) + { + fileName = fileName.substring(baseDirName.length()); + } + } + + return quotedString(fileName); + } + + + /** + * Returns a quoted version of the given string, if necessary. + */ + private String quotedString(String string) + { + return string.length() == 0 || + string.indexOf(' ') >= 0 || + string.indexOf('@') >= 0 || + string.indexOf('{') >= 0 || + string.indexOf('}') >= 0 || + string.indexOf('(') >= 0 || + string.indexOf(')') >= 0 || + string.indexOf(':') >= 0 || + string.indexOf(';') >= 0 || + string.indexOf(',') >= 0 ? ("'" + string + "'") : + ( string ); + } + + + /** + * A main method for testing configuration writing. + */ + public static void main(String[] args) { + try + { + ConfigurationWriter writer = new ConfigurationWriter(new File(args[0])); + + writer.write(new Configuration()); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/DataEntryReaderFactory.java b/src/proguard/DataEntryReaderFactory.java new file mode 100644 index 000000000..3bf6f568c --- /dev/null +++ b/src/proguard/DataEntryReaderFactory.java @@ -0,0 +1,141 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.io.*; +import proguard.util.*; + +import java.util.List; + + +/** + * This class can create DataEntryReader instances based on class path entries. + * The readers will unwrap the input data entries from any jars, wars, ears, + * and zips, before passing them to a given reader. + * + * @author Eric Lafortune + */ +public class DataEntryReaderFactory +{ + /** + * Creates a DataEntryReader that can read the given class path entry. + * + * @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 + * classes and resource files can be delegated. + * @return a DataEntryReader for reading the given class path entry. + */ + public static DataEntryReader createDataEntryReader(String messagePrefix, + ClassPathEntry classPathEntry, + DataEntryReader reader) + { + boolean isJar = classPathEntry.isJar(); + boolean isWar = classPathEntry.isWar(); + boolean isEar = classPathEntry.isEar(); + boolean isZip = classPathEntry.isZip(); + + List filter = classPathEntry.getFilter(); + List jarFilter = classPathEntry.getJarFilter(); + List warFilter = classPathEntry.getWarFilter(); + List earFilter = classPathEntry.getEarFilter(); + List zipFilter = classPathEntry.getZipFilter(); + + System.out.println(messagePrefix + + (isJar ? "jar" : + isWar ? "war" : + isEar ? "ear" : + isZip ? "zip" : + "directory") + + " [" + classPathEntry.getName() + "]" + + (filter != null || + jarFilter != null || + warFilter != null || + earFilter != null || + zipFilter != null ? " (filtered)" : "")); + + // Add a filter, if specified. + if (filter != null) + { + reader = new FilteredDataEntryReader( + new DataEntryNameFilter( + new ListParser(new FileNameParser()).parse(filter)), + reader); + } + + // Unzip any jars, if necessary. + reader = wrapInJarReader(reader, isJar, jarFilter, ".jar"); + if (!isJar) + { + // Unzip any wars, if necessary. + reader = wrapInJarReader(reader, isWar, warFilter, ".war"); + if (!isWar) + { + // Unzip any ears, if necessary. + reader = wrapInJarReader(reader, isEar, earFilter, ".ear"); + if (!isEar) + { + // Unzip any zips, if necessary. + reader = wrapInJarReader(reader, isZip, zipFilter, ".zip"); + } + } + } + + return reader; + } + + + /** + * Wraps the given DataEntryReader in a JarReader, filtering it if necessary. + */ + private static DataEntryReader wrapInJarReader(DataEntryReader reader, + boolean isJar, + List jarFilter, + String jarExtension) + { + // Unzip any jars, if necessary. + DataEntryReader jarReader = new JarReader(reader); + + if (isJar) + { + // Always unzip. + return jarReader; + } + else + { + // Add a filter, if specified. + if (jarFilter != null) + { + jarReader = new FilteredDataEntryReader( + new DataEntryNameFilter( + new ListParser(new FileNameParser()).parse(jarFilter)), + jarReader); + } + + // Only unzip the right type of jars. + return new FilteredDataEntryReader( + new DataEntryNameFilter( + new ExtensionMatcher(jarExtension)), + jarReader, + reader); + } + } +} diff --git a/src/proguard/DataEntryWriterFactory.java b/src/proguard/DataEntryWriterFactory.java new file mode 100644 index 000000000..ca33a3592 --- /dev/null +++ b/src/proguard/DataEntryWriterFactory.java @@ -0,0 +1,150 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.io.*; +import proguard.util.*; + +import java.util.List; + +/** + * This class can create DataEntryWriter instances based on class paths. The + * writers will wrap the output in the proper jars, wars, ears, and zips. + * + * @author Eric Lafortune + */ +public class DataEntryWriterFactory +{ + /** + * Creates a DataEntryWriter that can write to the given class path entries. + * + * @param classPath the output class path. + * @param fromIndex the start index in the class path. + * @param toIndex the end index in the class path. + * @return a DataEntryWriter for writing to the given class path entries. + */ + public static DataEntryWriter createDataEntryWriter(ClassPath classPath, + int fromIndex, + int toIndex) + { + DataEntryWriter writer = null; + + // Create a chain of writers, one for each class path entry. + for (int index = toIndex - 1; index >= fromIndex; index--) + { + ClassPathEntry entry = classPath.get(index); + writer = createClassPathEntryWriter(entry, writer); + } + + return writer; + } + + + /** + * Creates a DataEntryWriter that can write to the given class path entry, + * or delegate to another DataEntryWriter if its filters don't match. + */ + private static DataEntryWriter createClassPathEntryWriter(ClassPathEntry classPathEntry, + DataEntryWriter alternativeWriter) + { + boolean isJar = classPathEntry.isJar(); + boolean isWar = classPathEntry.isWar(); + boolean isEar = classPathEntry.isEar(); + boolean isZip = classPathEntry.isZip(); + + List filter = classPathEntry.getFilter(); + List jarFilter = classPathEntry.getJarFilter(); + List warFilter = classPathEntry.getWarFilter(); + List earFilter = classPathEntry.getEarFilter(); + List zipFilter = classPathEntry.getZipFilter(); + + System.out.println("Preparing output " + + (isJar ? "jar" : + isWar ? "war" : + isEar ? "ear" : + isZip ? "zip" : + "directory") + + " [" + classPathEntry.getName() + "]" + + (filter != null || + jarFilter != null || + warFilter != null || + earFilter != null || + zipFilter != null ? " (filtered)" : "")); + + DataEntryWriter writer = new DirectoryWriter(classPathEntry.getFile(), + isJar || + isWar || + isEar || + isZip); + + // Set up the filtered jar writers. + writer = wrapInJarWriter(writer, isZip, zipFilter, ".zip", isJar || isWar || isEar); + writer = wrapInJarWriter(writer, isEar, earFilter, ".ear", isJar || isWar); + writer = wrapInJarWriter(writer, isWar, warFilter, ".war", isJar); + writer = wrapInJarWriter(writer, isJar, jarFilter, ".jar", false); + + // Add a filter, if specified. + writer = filter != null? + new FilteredDataEntryWriter( + new DataEntryNameFilter( + new ListParser(new FileNameParser()).parse(filter)), + writer) : + writer; + + // Let the writer cascade, if specified. + return alternativeWriter != null ? + new CascadingDataEntryWriter(writer, alternativeWriter) : + writer; + } + + + /** + * Wraps the given DataEntryWriter in a JarWriter, filtering if necessary. + */ + private static DataEntryWriter wrapInJarWriter(DataEntryWriter writer, + boolean isJar, + List jarFilter, + String jarExtension, + boolean dontWrap) + { + // Zip up jars, if necessary. + DataEntryWriter jarWriter = dontWrap ? + (DataEntryWriter)new ParentDataEntryWriter(writer) : + (DataEntryWriter)new JarWriter(writer); + + // Add a filter, if specified. + DataEntryWriter filteredJarWriter = jarFilter != null? + new FilteredDataEntryWriter( + new DataEntryParentFilter( + new DataEntryNameFilter( + new ListParser(new FileNameParser()).parse(jarFilter))), + jarWriter) : + jarWriter; + + // Only zip up jars, unless the output is a jar file itself. + return new FilteredDataEntryWriter( + new DataEntryParentFilter( + new DataEntryNameFilter( + new ExtensionMatcher(jarExtension))), + filteredJarWriter, + isJar ? jarWriter : writer); + } +} diff --git a/src/proguard/DescriptorKeepChecker.java b/src/proguard/DescriptorKeepChecker.java new file mode 100644 index 000000000..fcd80b2bf --- /dev/null +++ b/src/proguard/DescriptorKeepChecker.java @@ -0,0 +1,169 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +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. + */ + 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 in + // class members that are being kept. + programClassPool.classesAccept( + new AllMemberVisitor( + new KeptMemberFilter(this))); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField 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) + { + 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) + { + int count = referencedClasses.length; + + // Adapt the count if the return type is a class type (not so + // pretty; assuming test just checks for final ';'). + if (ClassUtil.isInternalClassType(programMethod.getDescriptor(programClass))) + { + count--; + } + + for (int index = 0; index < count; index++) + { + if (referencedClasses[index] != null) + { + referencedClasses[index].accept(this); + } + } + } + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!KeepMarker.isKept(programClass)) + { + notePrinter.print(referencingClass.getName(), + programClass.getName(), + "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/DuplicateClassPrinter.java b/src/proguard/DuplicateClassPrinter.java new file mode 100644 index 000000000..7a071ce95 --- /dev/null +++ b/src/proguard/DuplicateClassPrinter.java @@ -0,0 +1,63 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassVisitor; + +/** + * This ClassVisitor writes out notes about the class files that it visits + * being duplicates. + * + * @author Eric Lafortune + */ +public class DuplicateClassPrinter implements ClassVisitor +{ + private final WarningPrinter notePrinter; + + + /** + * Creates a new DuplicateClassVisitor. + */ + public DuplicateClassPrinter(WarningPrinter notePrinter) + { + this.notePrinter = notePrinter; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + notePrinter.print(programClass.getName(), + "Note: duplicate definition of program class [" + + ClassUtil.externalClassName(programClass.getName()) + "]"); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + notePrinter.print(libraryClass.getName(), + "Note: duplicate definition of library class [" + + ClassUtil.externalClassName(libraryClass.getName()) + "]"); + } +} diff --git a/src/proguard/FileWordReader.java b/src/proguard/FileWordReader.java new file mode 100644 index 000000000..7309843e1 --- /dev/null +++ b/src/proguard/FileWordReader.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import java.net.URL; + + +/** + * A WordReader that returns words from a file or a URL. + * + * @author Eric Lafortune + */ +public class FileWordReader extends LineWordReader +{ + /** + * Creates a new FileWordReader for the given file. + */ + public FileWordReader(File file) throws IOException + { + super(new LineNumberReader(new BufferedReader(new FileReader(file))), + "file '" + file.getPath() + "'", + file.getParentFile() + ); + } + + + /** + * Creates a new FileWordReader for the given URL. + */ + public FileWordReader(URL url) throws IOException + { + super(new LineNumberReader(new BufferedReader(new InputStreamReader(url.openStream()))), + "file '" + url.toString() + "'", + null); + } +} diff --git a/src/proguard/FullyQualifiedClassNameChecker.java b/src/proguard/FullyQualifiedClassNameChecker.java new file mode 100644 index 000000000..eb1865a9d --- /dev/null +++ b/src/proguard/FullyQualifiedClassNameChecker.java @@ -0,0 +1,191 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 FullyQualifiedClassNameChecker. + */ + 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. + */ + 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.accepts(className)) + { + notePrinter.print(className, + "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 || + 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 new file mode 100644 index 000000000..51220b86b --- /dev/null +++ b/src/proguard/GPL.java @@ -0,0 +1,201 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import java.util.*; + +/** + * This class checks and prints out information about the GPL. + * + * @author Eric Lafortune + */ +public class GPL +{ + /** + * Prints out a note about the GPL if ProGuard is linked against unknown + * code. + */ + public static void check() + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Exception().printStackTrace(new PrintStream(out)); + LineNumberReader reader = new LineNumberReader( + new InputStreamReader( + new ByteArrayInputStream(out.toByteArray()))); + + Set unknownPackageNames = unknownPackageNames(reader); + + if (unknownPackageNames.size() > 0) + { + String uniquePackageNames = uniquePackageNames(unknownPackageNames); + + System.out.println("ProGuard is released under the GNU General Public License. You therefore"); + System.out.println("must ensure that programs that link to it ("+uniquePackageNames+"...)"); + System.out.println("carry the GNU General Public License as well. Alternatively, you can"); + System.out.println("apply for an exception with the author of ProGuard."); + } + } + + + /** + * Returns a set of package names from the given stack trace. + */ + private static Set unknownPackageNames(LineNumberReader reader) + { + Set packageNames = new HashSet(); + + try + { + while (true) + { + String line = reader.readLine(); + if (line == null) + { + break; + } + + line = line.trim(); + if (line.startsWith("at ")) + { + line = line.substring(2).trim(); + line = trimSuffix(line, '('); + line = trimSuffix(line, '.'); + line = trimSuffix(line, '.'); + + if (line.length() > 0 && !isKnown(line)) + { + packageNames.add(line); + } + } + } + } + catch (IOException ex) + { + // We'll just stop looking for more names. + } + + return packageNames; + } + + + /** + * Returns a comma-separated list of package names from the set, excluding + * any subpackages of packages in the set. + */ + private static String uniquePackageNames(Set packageNames) + { + StringBuffer buffer = new StringBuffer(); + + Iterator iterator = packageNames.iterator(); + while (iterator.hasNext()) + { + String packageName = (String)iterator.next(); + if (!containsPrefix(packageNames, packageName)) + { + buffer.append(packageName).append(", "); + } + } + + return buffer.toString(); + } + + + /** + * Returns a given string without the suffix, as defined by the given + * separator. + */ + private static String trimSuffix(String string, char separator) + { + int index = string.lastIndexOf(separator); + return index < 0 ? "" : string.substring(0, index); + } + + + /** + * Returns whether the given set contains a prefix of the given name. + */ + private static boolean containsPrefix(Set set, String name) + { + int index = 0; + + while (!set.contains(name.substring(0, index))) + { + index = name.indexOf('.', index + 1); + if (index < 0) + { + return false; + } + } + + return true; + } + + + /** + * Returns whether the given package name has been granted an exception + * against the GPL linking clause, by the copyright holder of ProGuard. + * This method is not legally binding, but of course the actual license is. + * Please contact the copyright holder if you would like an exception for + * your code as well. + */ + private static boolean isKnown(String packageName) + { + return packageName.startsWith("java") || + packageName.startsWith("sun.reflect") || + packageName.startsWith("proguard") || + packageName.startsWith("org.apache.tools.ant") || + packageName.startsWith("org.apache.tools.maven") || + packageName.startsWith("org.gradle") || + packageName.startsWith("org.codehaus.groovy") || + packageName.startsWith("org.eclipse") || + packageName.startsWith("org.netbeans") || + packageName.startsWith("com.android") || + packageName.startsWith("com.sun.kvem") || + packageName.startsWith("com.intel") || + packageName.startsWith("net.certiv.proguarddt") || + packageName.startsWith("groovy") || + packageName.startsWith("scala") || + packageName.startsWith("sbt") || + packageName.startsWith("xsbt") || + packageName.startsWith("eclipseme") || + packageName.startsWith("com.neomades") || + packageName.startsWith("jg.j2me") || + packageName.startsWith("jg.common") || + packageName.startsWith("jg.buildengine"); + } + + + public static void main(String[] args) + { + LineNumberReader reader = new LineNumberReader( + new InputStreamReader(System.in)); + + Set unknownPackageNames = unknownPackageNames(reader); + + if (unknownPackageNames.size() > 0) + { + String uniquePackageNames = uniquePackageNames(unknownPackageNames); + + System.out.println(uniquePackageNames); + } + } +} diff --git a/src/proguard/Initializer.java b/src/proguard/Initializer.java new file mode 100644 index 000000000..c56daa732 --- /dev/null +++ b/src/proguard/Initializer.java @@ -0,0 +1,433 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.visitor.AllAttributeVisitor; +import proguard.classfile.constant.visitor.*; +import proguard.classfile.instruction.visitor.AllInstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.util.*; + +import java.io.IOException; +import java.util.*; + +/** + * This class initializes class pools. + * + * @author Eric Lafortune + */ +public class Initializer +{ + private final Configuration configuration; + + + /** + * Creates a new Initializer to initialize classes according to the given + * configuration. + */ + public Initializer(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Initializes the classes in the given program class pool and library class + * pool, performs some basic checks, and shrinks the library class pool. + */ + public void execute(ClassPool programClassPool, + ClassPool libraryClassPool) throws IOException + { + int originalLibraryClassPoolSize = libraryClassPool.size(); + + // Perform a basic check on the keep options in the configuration. + WarningPrinter keepClassMemberNotePrinter = new WarningPrinter(System.out, configuration.note); + + new KeepClassMemberChecker(keepClassMemberNotePrinter).checkClassSpecifications(configuration.keep); + + // Construct a reduced 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. + ClassPool reducedLibraryClassPool = configuration.useUniqueClassMemberNames ? + null : new ClassPool(); + + WarningPrinter classReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); + WarningPrinter dependencyWarningPrinter = new WarningPrinter(System.err, configuration.warn); + + // Initialize the superclass hierarchies for program classes. + programClassPool.classesAccept( + new ClassSuperHierarchyInitializer(programClassPool, + libraryClassPool, + classReferenceWarningPrinter, + null)); + + // Initialize the superclass hierarchy of all library classes, without + // warnings. + libraryClassPool.classesAccept( + new ClassSuperHierarchyInitializer(programClassPool, + libraryClassPool, + null, + dependencyWarningPrinter)); + + // Initialize the class references of program class members and + // attributes. Note that all superclass hierarchies have to be + // initialized for this purpose. + WarningPrinter memberReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); + + programClassPool.classesAccept( + new ClassReferenceInitializer(programClassPool, + libraryClassPool, + classReferenceWarningPrinter, + memberReferenceWarningPrinter, + null)); + + if (reducedLibraryClassPool != null) + { + // Collect the library classes that are directly referenced by + // program classes, without introspection. + programClassPool.classesAccept( + new ReferencedClassVisitor( + new LibraryClassFilter( + new ClassPoolFiller(reducedLibraryClassPool)))); + + // Reinitialize the superclass hierarchies of referenced library + // classes, this time with warnings. + reducedLibraryClassPool.classesAccept( + new ClassSuperHierarchyInitializer(programClassPool, + libraryClassPool, + classReferenceWarningPrinter, + null)); + } + + // Initialize the enum annotation references. + programClassPool.classesAccept( + new AllAttributeVisitor(true, + new AllElementValueVisitor(true, + new EnumFieldReferenceInitializer()))); + + // Initialize the Class.forName references. + WarningPrinter dynamicClassReferenceNotePrinter = new WarningPrinter(System.out, configuration.note); + WarningPrinter classForNameNotePrinter = new WarningPrinter(System.out, configuration.note); + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new DynamicClassReferenceInitializer(programClassPool, + libraryClassPool, + dynamicClassReferenceNotePrinter, + null, + classForNameNotePrinter, + createClassNoteExceptionMatcher(configuration.keep)))))); + + // Initialize the Class.get[Declared]{Field,Method} references. + WarningPrinter getMemberNotePrinter = new WarningPrinter(System.out, configuration.note); + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new DynamicMemberReferenceInitializer(programClassPool, + libraryClassPool, + getMemberNotePrinter, + createClassMemberNoteExceptionMatcher(configuration.keep, true), + createClassMemberNoteExceptionMatcher(configuration.keep, false)))))); + + // Initialize other string constant references, if requested. + if (configuration.adaptClassStrings != null) + { + programClassPool.classesAccept( + new ClassNameFilter(configuration.adaptClassStrings, + new AllConstantVisitor( + new StringReferenceInitializer(programClassPool, + libraryClassPool)))); + } + + // Print various notes, if specified. + WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningPrinter(System.out, configuration.note); + WarningPrinter descriptorKeepNotePrinter = new WarningPrinter(System.out, configuration.note); + + new FullyQualifiedClassNameChecker(programClassPool, + libraryClassPool, + fullyQualifiedClassNameNotePrinter).checkClassSpecifications(configuration.keep); + + new DescriptorKeepChecker(programClassPool, + libraryClassPool, + descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep); + + // Initialize the class references of library class members. + if (reducedLibraryClassPool != null) + { + // Collect the library classes that are referenced by program + // classes, directly or indirectly, with or without introspection. + programClassPool.classesAccept( + new ReferencedClassVisitor( + new LibraryClassFilter( + new ClassHierarchyTraveler(true, true, true, false, + new LibraryClassFilter( + new ClassPoolFiller(reducedLibraryClassPool)))))); + + // Initialize the class references of referenced library + // classes, without warnings. + reducedLibraryClassPool.classesAccept( + new ClassReferenceInitializer(programClassPool, + libraryClassPool, + null, + null, + dependencyWarningPrinter)); + + // 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. + reducedLibraryClassPool.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))))) + })); + } + else + { + // Initialize the class references of all library class members. + libraryClassPool.classesAccept( + new ClassReferenceInitializer(programClassPool, + libraryClassPool, + null, + null, + dependencyWarningPrinter)); + } + + // 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. + 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."); + } + + 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')."); + } + + int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount(); + if (dynamicClassReferenceNoteCount > 0) + { + System.out.println("Note: there were " + dynamicClassReferenceNoteCount + + " unresolved dynamic references to classes or interfaces."); + System.out.println(" You should check if you need to specify additional program jars."); + } + + int classForNameNoteCount = classForNameNotePrinter.getWarningCount(); + if (classForNameNoteCount > 0) + { + System.out.println("Note: there were " + classForNameNoteCount + + " class casts of dynamically created class instances."); + System.out.println(" You might consider explicitly keeping the mentioned classes and/or"); + System.out.println(" their implementations (using '-keep')."); + } + + int getmemberNoteCount = getMemberNotePrinter.getWarningCount(); + if (getmemberNoteCount > 0) + { + System.out.println("Note: there were " + getmemberNoteCount + + " accesses to class members by means of introspection."); + System.out.println(" You should consider explicitly keeping the mentioned class members"); + System.out.println(" (using '-keep' or '-keepclassmembers')."); + } + + // Print out a summary of the warnings, if necessary. + int classReferenceWarningCount = classReferenceWarningPrinter.getWarningCount(); + if (classReferenceWarningCount > 0) + { + System.err.println("Warning: there were " + classReferenceWarningCount + + " unresolved references to classes or interfaces."); + System.err.println(" You may need to add missing library jars or update their versions."); + System.err.println(" If your code works fine without the missing classes, you can suppress"); + System.err.println(" the warnings with '-dontwarn' options."); + + if (configuration.skipNonPublicLibraryClasses) + { + System.err.println(" You may also have to remove the option '-skipnonpubliclibraryclasses'."); + } + } + + int dependencyWarningCount = dependencyWarningPrinter.getWarningCount(); + if (dependencyWarningCount > 0) + { + System.err.println("Warning: there were " + dependencyWarningCount + + " instances of library classes depending on program classes."); + System.err.println(" You must avoid such dependencies, since the program classes will"); + System.err.println(" be processed, while the library classes will remain unchanged."); + } + + int memberReferenceWarningCount = memberReferenceWarningPrinter.getWarningCount(); + if (memberReferenceWarningCount > 0) + { + System.err.println("Warning: there were " + memberReferenceWarningCount + + " unresolved references to program class members."); + System.err.println(" Your input classes appear to be inconsistent."); + System.err.println(" You may need to recompile the code or update the library versions."); + System.err.println(" Alternatively, you may have to specify the option "); + System.err.println(" '-dontskipnonpubliclibraryclassmembers'."); + + if (configuration.skipNonPublicLibraryClasses) + { + System.err.println(" You may also have to remove the option '-skipnonpubliclibraryclasses'."); + } + } + + if ((classReferenceWarningCount > 0 || + dependencyWarningCount > 0 || + memberReferenceWarningCount > 0) && + !configuration.ignoreWarnings) + { + throw new IOException("Please correct the above warnings first."); + } + + if ((configuration.note == null || + !configuration.note.isEmpty()) && + (configuration.warn != null && + configuration.warn.isEmpty() || + configuration.ignoreWarnings)) + { + System.out.println("Note: You're ignoring all warnings!"); + } + + // Discard unused library classes. + if (configuration.verbose) + { + System.out.println("Ignoring unused library classes..."); + System.out.println(" Original number of library classes: " + originalLibraryClassPoolSize); + System.out.println(" Final number of library classes: " + libraryClassPool.size()); + } + } + + + /** + * Extracts a list of exceptions of classes for which not to print notes, + * from the keep configuration. + */ + private StringMatcher createClassNoteExceptionMatcher(List noteExceptions) + { + if (noteExceptions != null) + { + List noteExceptionNames = new ArrayList(noteExceptions.size()); + for (int index = 0; index < noteExceptions.size(); index++) + { + KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); + if (keepClassSpecification.markClasses) + { + // If the class itself is being kept, it's ok. + String className = keepClassSpecification.className; + if (className != null) + { + noteExceptionNames.add(className); + } + + // If all of its extensions are being kept, it's ok too. + String extendsClassName = keepClassSpecification.extendsClassName; + if (extendsClassName != null) + { + noteExceptionNames.add(extendsClassName); + } + } + } + + if (noteExceptionNames.size() > 0) + { + 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++) + { + KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); + List memberSpecifications = isField ? + keepClassSpecification.fieldSpecifications : + keepClassSpecification.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); + } + } + + return null; + } +} diff --git a/src/proguard/InputReader.java b/src/proguard/InputReader.java new file mode 100644 index 000000000..8478c8ffe --- /dev/null +++ b/src/proguard/InputReader.java @@ -0,0 +1,233 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; +import proguard.classfile.util.WarningPrinter; +import proguard.classfile.visitor.*; +import proguard.io.*; + +import java.io.IOException; + +/** + * This class reads the input class files. + * + * @author Eric Lafortune + */ +public class InputReader +{ + private final Configuration configuration; + + + /** + * Creates a new InputReader to read input class files as specified by the + * given configuration. + */ + public InputReader(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Fills the given program class pool and library class pool by reading + * class files, based on the current configuration. + */ + public void execute(ClassPool programClassPool, + ClassPool libraryClassPool) throws IOException + { + // Check if we have at least some input classes. + if (configuration.programJars == null) + { + throw new IOException("The input is empty. You have to specify one or more '-injars' options"); + } + + // Perform some sanity checks on the class paths. + checkInputOutput(configuration.libraryJars, + configuration.programJars); + checkInputOutput(configuration.programJars, + configuration.programJars); + + WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn); + WarningPrinter notePrinter = new WarningPrinter(System.out, configuration.note); + + DuplicateClassPrinter duplicateClassPrinter = new DuplicateClassPrinter(notePrinter); + + // Read the program class files. + // Prepare a data entry reader to filter all classes, + // which are then decoded to classes by a class reader, + // which are then put in the class pool by a class pool filler. + readInput("Reading program ", + configuration.programJars, + 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. + if (programClassPool.size() == 0) + { + throw new IOException("The input doesn't contain any classes. Did you specify the proper '-injars' options?"); + } + + // Read the library class files, if any. + if (configuration.libraryJars != null) + { + // 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 library ", + configuration.libraryJars, + 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. + int noteCount = notePrinter.getWarningCount(); + if (noteCount > 0) + { + System.err.println("Note: there were " + noteCount + + " duplicate class definitions."); + } + + // Print out a summary of the warnings, if necessary. + int warningCount = warningPrinter.getWarningCount(); + if (warningCount > 0) + { + System.err.println("Warning: there were " + warningCount + + " classes in incorrectly named files."); + System.err.println(" You should make sure all file names correspond to their class names."); + System.err.println(" The directory hierarchies must correspond to the package hierarchies."); + + if (!configuration.ignoreWarnings) + { + System.err.println(" If you don't mind the mentioned classes not being written out,"); + System.err.println(" you could try your luck using the '-ignorewarnings' option."); + throw new IOException("Please correct the above warnings first."); + } + } + } + + + /** + * Performs some sanity checks on the class paths. + */ + private void checkInputOutput(ClassPath inputClassPath, + ClassPath outputClassPath) + throws IOException + { + if (inputClassPath == null || + outputClassPath == null) + { + return; + } + + for (int index1 = 0; index1 < inputClassPath.size(); index1++) + { + ClassPathEntry entry1 = inputClassPath.get(index1); + if (!entry1.isOutput()) + { + for (int index2 = 0; index2 < outputClassPath.size(); index2++) + { + ClassPathEntry entry2 = outputClassPath.get(index2); + if (entry2.isOutput() && + entry2.getName().equals(entry1.getName())) + { + throw new IOException("Input jars and output jars must be different ["+entry1.getName()+"]"); + } + } + } + } + } + + + /** + * Reads all input entries from the given class path. + */ + private void readInput(String messagePrefix, + ClassPath classPath, + DataEntryReader reader) throws IOException + { + readInput(messagePrefix, + classPath, + 0, + classPath.size(), + reader); + } + + + /** + * Reads all input entries from the given section of the given class path. + */ + public void readInput(String messagePrefix, + ClassPath classPath, + int fromIndex, + int toIndex, + DataEntryReader reader) throws IOException + { + for (int index = fromIndex; index < toIndex; index++) + { + ClassPathEntry entry = classPath.get(index); + if (!entry.isOutput()) + { + readInput(messagePrefix, entry, reader); + } + } + } + + + /** + * Reads the given input class path entry. + */ + private void readInput(String messagePrefix, + ClassPathEntry classPathEntry, + DataEntryReader dataEntryReader) throws IOException + { + try + { + // Create a reader that can unwrap jars, wars, ears, and zips. + DataEntryReader reader = + DataEntryReaderFactory.createDataEntryReader(messagePrefix, + classPathEntry, + dataEntryReader); + + // Create the data entry pump. + DirectoryPump directoryPump = + new DirectoryPump(classPathEntry.getFile()); + + // Pump the data entries into the reader. + directoryPump.pumpDataEntries(reader); + } + catch (IOException ex) + { + throw (IOException)new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")").initCause(ex); + } + } +} diff --git a/src/proguard/KeepClassMemberChecker.java b/src/proguard/KeepClassMemberChecker.java new file mode 100644 index 000000000..3a375da59 --- /dev/null +++ b/src/proguard/KeepClassMemberChecker.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.*; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.List; + +/** + * This class checks if the user has forgotten to specify class members in + * some keep options in the configuration. + * + * @author Eric Lafortune + */ +public class KeepClassMemberChecker +extends SimplifiedVisitor +implements ClassVisitor +{ + private final WarningPrinter notePrinter; + + + /** + * Creates a new KeepClassMemberChecker. + */ + public KeepClassMemberChecker(WarningPrinter notePrinter) + { + this.notePrinter = notePrinter; + } + + + /** + * Checks if the given class specifications try to keep class members + * without actually specifying any, printing notes if necessary. Returns + * the number of notes printed. + */ + public void checkClassSpecifications(List keepClassSpecifications) + { + if (keepClassSpecifications != null) + { + for (int index = 0; index < keepClassSpecifications.size(); index++) + { + KeepClassSpecification keepClassSpecification = + (KeepClassSpecification)keepClassSpecifications.get(index); + + if (!keepClassSpecification.markClasses && + (keepClassSpecification.fieldSpecifications == null || + keepClassSpecification.fieldSpecifications.size() == 0) && + (keepClassSpecification.methodSpecifications == null || + keepClassSpecification.methodSpecifications.size() == 0)) + { + String className = keepClassSpecification.className; + if (className == null) + { + className = keepClassSpecification.extendsClassName; + } + + if (className != null && + notePrinter.accepts(className)) + { + notePrinter.print(className, + "Note: the configuration doesn't specify which class members to keep for class '" + + ClassUtil.externalClassName(className) + "'"); + } + } + } + } + } +} diff --git a/src/proguard/KeepClassSpecification.java b/src/proguard/KeepClassSpecification.java new file mode 100644 index 000000000..a6215c5ce --- /dev/null +++ b/src/proguard/KeepClassSpecification.java @@ -0,0 +1,137 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 represents a keep option with class specification. + * + * @author Eric Lafortune + */ +public class KeepClassSpecification 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 KeepClassSpecification for all possible classes. + * @param markClasses specifies whether to mark the classes. + * If false, only class members are marked. + * If true, the classes are marked as well. + * @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 KeepClassSpecification(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 KeepClassSpecification. + * @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 KeepClassSpecification(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; + } + + KeepClassSpecification other = (KeepClassSpecification)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/LineWordReader.java b/src/proguard/LineWordReader.java new file mode 100644 index 000000000..64a228c30 --- /dev/null +++ b/src/proguard/LineWordReader.java @@ -0,0 +1,74 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + + +/** + * A WordReader that returns words from a line number reader. + * + * @author Eric Lafortune + */ +public class LineWordReader extends WordReader +{ + private final LineNumberReader reader; + private final String description; + + + /** + * Creates a new LineWordReader for the given input. + */ + public LineWordReader(LineNumberReader lineNumberReader, + String description, + File baseDir) throws IOException + { + super(baseDir); + + this.reader = lineNumberReader; + this.description = description; + } + + + // Implementations for WordReader. + + protected String nextLine() throws IOException + { + return reader.readLine(); + } + + + protected String lineLocationDescription() + { + return "line " + reader.getLineNumber() + " of " + description; + } + + + public void close() throws IOException + { + super.close(); + + if (reader != null) + { + reader.close(); + } + } +} diff --git a/src/proguard/MANIFEST.MF b/src/proguard/MANIFEST.MF new file mode 100644 index 000000000..3bad15ec1 --- /dev/null +++ b/src/proguard/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Main-Class: proguard.ProGuard diff --git a/src/proguard/MemberSpecification.java b/src/proguard/MemberSpecification.java new file mode 100644 index 000000000..e771fdea3 --- /dev/null +++ b/src/proguard/MemberSpecification.java @@ -0,0 +1,114 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 class members. The specification is + * template-based: the class member names and descriptors can contain wildcards. + * + * @author Eric Lafortune + */ +public class MemberSpecification +{ + public int requiredSetAccessFlags; + public int requiredUnsetAccessFlags; + public final String annotationType; + public final String name; + public final String descriptor; + + + /** + * Creates a new option to keep all possible class members. + */ + public MemberSpecification() + { + this(0, + 0, + null, + null, + null); + } + + + /** + * Creates a new option to keep the specified class member(s). + * + * @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 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. + * @param descriptor the class member descriptor. The + * descriptor may be null to specify any + * class member or it may contain + * "**", "*", or "?" wildcards. + */ + 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; + } + + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (object == null || + this.getClass() != object.getClass()) + { + return false; + } + + MemberSpecification other = (MemberSpecification)object; + return + (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 ) ^ + (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 new file mode 100644 index 000000000..c4467cf25 --- /dev/null +++ b/src/proguard/OutputWriter.java @@ -0,0 +1,296 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; +import proguard.classfile.util.ClassUtil; +import proguard.io.*; + +import java.io.IOException; +import java.util.*; + +/** + * This class writes the output class files. + * + * @author Eric Lafortune + */ +public class OutputWriter +{ + private final Configuration configuration; + + + /** + * Creates a new OutputWriter to write output class files as specified by + * the given configuration. + */ + public OutputWriter(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Writes the given class pool to class files, based on the current + * configuration. + */ + public void execute(ClassPool programClassPool) throws IOException + { + ClassPath programJars = configuration.programJars; + + // Perform a check on the first jar. + ClassPathEntry firstEntry = programJars.get(0); + if (firstEntry.isOutput()) + { + throw new IOException("The output jar [" + firstEntry.getName() + + "] must be specified after an input jar, or it will be empty."); + } + + // Check if the first of two subsequent the output jars has a filter. + for (int index = 0; index < programJars.size() - 1; index++) + { + ClassPathEntry entry = programJars.get(index); + if (entry.isOutput()) + { + if (entry.getFilter() == null && + entry.getJarFilter() == null && + entry.getWarFilter() == null && + entry.getEarFilter() == null && + entry.getZipFilter() == null && + programJars.get(index + 1).isOutput()) + { + throw new IOException("The output jar [" + entry.getName() + + "] must have a filter, or all subsequent output jars will be empty."); + } + } + } + + // Check if the output jar names are different from the input jar names. + for (int outIndex = 0; outIndex < programJars.size() - 1; outIndex++) + { + ClassPathEntry entry = programJars.get(outIndex); + if (entry.isOutput()) + { + for (int inIndex = 0; inIndex < programJars.size(); inIndex++) + { + ClassPathEntry otherEntry = programJars.get(inIndex); + + if (!otherEntry.isOutput() && + entry.getFile().equals(otherEntry.getFile())) + { + throw new IOException("The output jar [" + entry.getName() + + "] must be different from all input jars."); + } + } + } + } + + // Check for potential problems with mixed-case class names on + // case-insensitive file systems. + if (configuration.obfuscate && + configuration.useMixedCaseClassNames && + configuration.classObfuscationDictionary == null && + (configuration.note == null || + !configuration.note.isEmpty())) + { + String os = System.getProperty("os.name").toLowerCase(); + if (os.startsWith("windows") || + os.startsWith("mac os")) + { + // Go over all program class path entries. + for (int index = 0; index < programJars.size(); index++) + { + // Is it an output directory? + ClassPathEntry entry = programJars.get(index); + if (entry.isOutput() && + !entry.isJar() && + !entry.isWar() && + !entry.isEar() && + !entry.isZip()) + { + System.out.println("Note: you're writing the processed class files to a directory [" + entry.getName() +"]."); + System.out.println(" This will likely cause problems with obfuscated mixed-case class names."); + System.out.println(" You should consider writing the output to a jar file, or otherwise"); + System.out.println(" specify '-dontusemixedcaseclassnames'."); + + break; + } + } + } + } + + int firstInputIndex = 0; + int lastInputIndex = 0; + + // Go over all program class path entries. + for (int index = 0; index < programJars.size(); index++) + { + // Is it an input entry? + ClassPathEntry entry = programJars.get(index); + if (!entry.isOutput()) + { + // Remember the index of the last input entry. + lastInputIndex = index; + } + else + { + // Check if this the last output entry in a series. + int nextIndex = index + 1; + if (nextIndex == programJars.size() || + !programJars.get(nextIndex).isOutput()) + { + // Write the processed input entries to the output entries. + writeOutput(programClassPool, + programJars, + firstInputIndex, + lastInputIndex + 1, + nextIndex); + + // Start with the next series of input entries. + firstInputIndex = nextIndex; + } + } + } + } + + + /** + * Transfers the specified input jars to the specified output jars. + */ + private void writeOutput(ClassPool programClassPool, + ClassPath classPath, + int fromInputIndex, + int fromOutputIndex, + int toOutputIndex) + throws IOException + { + try + { + // Construct the writer that can write jars, wars, ears, zips, and + // directories, cascading over the specified output entries. + DataEntryWriter writer = + DataEntryWriterFactory.createDataEntryWriter(classPath, + fromOutputIndex, + toOutputIndex); + + // 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 resourceCopier = + new DataEntryCopier(writer); + + DataEntryReader resourceRewriter = resourceCopier; + + // Wrap the resource writer with a filter and a data entry rewriter, + // if required. + if (configuration.adaptResourceFileContents != null) + { + resourceRewriter = + new NameFilter(configuration.adaptResourceFileContents, + new NameFilter("META-INF/MANIFEST.MF,META-INF/*.SF", + new ManifestRewriter(programClassPool, writer), + new DataEntryRewriter(programClassPool, writer)), + resourceRewriter); + } + + // Wrap the resource writer with a filter and a data entry renamer, + // if required. + if (configuration.adaptResourceFileNames != null) + { + Map packagePrefixMap = createPackagePrefixMap(programClassPool); + + resourceRewriter = + new NameFilter(configuration.adaptResourceFileNames, + new DataEntryObfuscator(programClassPool, + packagePrefixMap, + resourceRewriter), + resourceRewriter); + } + + DataEntryReader directoryRewriter = null; + + // Wrap the directory writer with a filter and a data entry renamer, + // if required. + if (configuration.keepDirectories != null) + { + Map packagePrefixMap = createPackagePrefixMap(programClassPool); + + directoryRewriter = + new NameFilter(configuration.keepDirectories, + new DataEntryRenamer(packagePrefixMap, + resourceCopier, + resourceCopier)); + } + + // Create the reader that can write class files and copy directories + // and resource files to the main writer. + DataEntryReader reader = + new ClassFilter( classRewriter, + new DirectoryFilter(directoryRewriter, + resourceRewriter)); + + // Go over the specified input entries and write their processed + // versions. + new InputReader(configuration).readInput(" Copying resources from program ", + classPath, + fromInputIndex, + fromOutputIndex, + reader); + + // Close all output entries. + writer.close(); + } + catch (IOException ex) + { + throw (IOException)new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")").initCause(ex); + } + } + + + /** + * Creates a map of old package prefixes to new package prefixes, based on + * the given class pool. + */ + private static Map createPackagePrefixMap(ClassPool classPool) + { + Map packagePrefixMap = new HashMap(); + + Iterator iterator = classPool.classNames(); + while (iterator.hasNext()) + { + String className = (String)iterator.next(); + String packagePrefix = ClassUtil.internalPackagePrefix(className); + + String mappedNewPackagePrefix = (String)packagePrefixMap.get(packagePrefix); + if (mappedNewPackagePrefix == null || + !mappedNewPackagePrefix.equals(packagePrefix)) + { + String newClassName = classPool.getClass(className).getName(); + String newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName); + + packagePrefixMap.put(packagePrefix, newPackagePrefix); + } + } + + return packagePrefixMap; + } +} diff --git a/src/proguard/ParseException.java b/src/proguard/ParseException.java new file mode 100644 index 000000000..b4af6c27b --- /dev/null +++ b/src/proguard/ParseException.java @@ -0,0 +1,51 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 Exception signals that a parse exception of some + * sort has occurred. + * + * @author Eric Lafortune + */ +public class ParseException extends Exception { + + /** + * Constructs a ParseException with null + * as its error detail message. + */ + public ParseException() { + super(); + } + + /** + * Constructs a ParseException with the specified detail + * message. The error message string s can later be + * retrieved by the {@link Throwable#getMessage} + * method of class Throwable. + * + * @param s the detail message. + */ + public ParseException(String s) { + super(s); + } +} diff --git a/src/proguard/ProGuard.java b/src/proguard/ProGuard.java new file mode 100644 index 000000000..4410174bb --- /dev/null +++ b/src/proguard/ProGuard.java @@ -0,0 +1,504 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; +import proguard.classfile.editor.ClassElementSorter; +import proguard.classfile.visitor.*; +import proguard.obfuscate.Obfuscator; +import proguard.optimize.*; +import proguard.preverify.*; +import proguard.shrink.Shrinker; + +import java.io.*; +import java.util.Properties; + +/** + * Tool for shrinking, optimizing, obfuscating, and preverifying Java classes. + * + * @author Eric Lafortune + */ +public class ProGuard +{ + public static final String VERSION = "ProGuard, version 4.9"; + + private final Configuration configuration; + private ClassPool programClassPool = new ClassPool(); + private final ClassPool libraryClassPool = new ClassPool(); + + + /** + * Creates a new ProGuard object to process jars as specified by the given + * configuration. + */ + public ProGuard(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Performs all subsequent ProGuard operations. + */ + public void execute() throws IOException + { + System.out.println(VERSION); + + GPL.check(); + + if (configuration.printConfiguration != null) + { + printConfiguration(); + } + + if (configuration.programJars != null && + configuration.programJars.hasOutput() && + new UpToDateChecker(configuration).check()) + { + return; + } + + readInput(); + + if (configuration.printSeeds != null || + configuration.shrink || + configuration.optimize || + configuration.obfuscate || + configuration.preverify) + { + initialize(); + } + + if (configuration.targetClassVersion != 0) + { + target(); + } + + if (configuration.printSeeds != null) + { + printSeeds(); + } + + if (configuration.shrink) + { + shrink(); + } + + if (configuration.preverify) + { + inlineSubroutines(); + } + + if (configuration.optimize) + { + for (int optimizationPass = 0; + optimizationPass < configuration.optimizationPasses; + optimizationPass++) + { + 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(); + } + } + } + + if (configuration.obfuscate) + { + obfuscate(); + } + + if (configuration.preverify) + { + preverify(); + } + + if (configuration.shrink || + configuration.optimize || + configuration.obfuscate || + configuration.preverify) + { + sortClassElements(); + } + + if (configuration.programJars.hasOutput()) + { + writeOutput(); + } + + if (configuration.dump != null) + { + dump(); + } + } + + + /** + * 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 + { + if (configuration.verbose) + { + System.out.println("Reading input..."); + } + + // Fill the program class pool and the library class pool. + new InputReader(configuration).execute(programClassPool, libraryClassPool); + } + + + /** + * Initializes the cross-references between all classes, performs some + * basic checks, and shrinks the library class pool. + */ + private void initialize() throws IOException + { + if (configuration.verbose) + { + System.out.println("Initializing..."); + } + + new Initializer(configuration).execute(programClassPool, libraryClassPool); + } + + + /** + * 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. + */ + private void printSeeds() throws IOException + { + if (configuration.verbose) + { + System.out.println("Printing kept classes, fields, and methods..."); + } + + PrintStream ps = createPrintStream(configuration.printSeeds); + try + { + new SeedPrinter(ps).write(configuration, programClassPool, libraryClassPool); + } + finally + { + closePrintStream(ps); + } + } + + + /** + * Performs the shrinking step. + */ + private void shrink() throws IOException + { + if (configuration.verbose) + { + System.out.println("Shrinking..."); + + // We'll print out some explanation, if requested. + if (configuration.whyAreYouKeeping != null) + { + System.out.println("Explaining why classes and class members are being kept..."); + } + + // We'll print out the usage, if requested. + if (configuration.printUsage != null) + { + System.out.println("Printing usage to [" + fileName(configuration.printUsage) + "]..."); + } + } + + // Perform the actual shrinking. + programClassPool = + new Shrinker(configuration).execute(programClassPool, libraryClassPool); + } + + + /** + * Performs the subroutine inlining step. + */ + private void inlineSubroutines() + { + if (configuration.verbose) + { + System.out.println("Inlining subroutines..."); + } + + // Perform the actual inlining. + new SubroutineInliner(configuration).execute(programClassPool); + } + + + /** + * Performs the optimization step. + */ + private boolean optimize() throws IOException + { + if (configuration.verbose) + { + System.out.println("Optimizing..."); + } + + // Perform the actual optimization. + return new Optimizer(configuration).execute(programClassPool, libraryClassPool); + } + + + /** + * Performs the obfuscation step. + */ + private void obfuscate() throws IOException + { + if (configuration.verbose) + { + System.out.println("Obfuscating..."); + + // We'll apply a mapping, if requested. + if (configuration.applyMapping != null) + { + 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 to [" + fileName(configuration.printMapping) + "]..."); + } + } + + // Perform the actual obfuscation. + new Obfuscator(configuration).execute(programClassPool, libraryClassPool); + } + + + /** + * 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 sortClassElements() + { + programClassPool.classesAccept(new ClassElementSorter()); + } + + + /** + * Writes the output class files. + */ + private void writeOutput() throws IOException + { + if (configuration.verbose) + { + System.out.println("Writing output..."); + } + + // Write out the program class pool. + new OutputWriter(configuration).execute(programClassPool); + } + + + /** + * Prints out the contents of the program classes. + */ + private void dump() throws IOException + { + if (configuration.verbose) + { + System.out.println("Printing classes to [" + fileName(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 file == Configuration.STD_OUT ? System.out : + new PrintStream( + new BufferedOutputStream( + new FileOutputStream(file))); + } + + + /** + * 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 + { + printStream.close(); + } + } + + + /** + * Returns the canonical file name for the given file, or "standard output" + * if the file name is empty. + */ + private String fileName(File file) + { + if (file == Configuration.STD_OUT) + { + return "standard output"; + } + else + { + try + { + return file.getCanonicalPath(); + } + catch (IOException ex) + { + return file.getPath(); + } + } + } + + + /** + * The main method for ProGuard. + */ + public static void main(String[] args) + { + if (args.length == 0) + { + System.out.println(VERSION); + System.out.println("Usage: java proguard.ProGuard [options ...]"); + System.exit(1); + } + + // Create the default options. + Configuration configuration = new Configuration(); + + try + { + // Parse the options specified in the command line arguments. + ConfigurationParser parser = new ConfigurationParser(args, + System.getProperties()); + try + { + parser.parse(configuration); + } + finally + { + parser.close(); + } + + // Execute ProGuard with these options. + new ProGuard(configuration).execute(); + } + catch (Exception ex) + { + if (configuration.verbose) + { + // Print a verbose stack trace. + ex.printStackTrace(); + } + else + { + // Print just the stack trace message. + System.err.println("Error: "+ex.getMessage()); + } + + System.exit(1); + } + + System.exit(0); + } +} diff --git a/src/proguard/SeedPrinter.java b/src/proguard/SeedPrinter.java new file mode 100644 index 000000000..8ed74b610 --- /dev/null +++ b/src/proguard/SeedPrinter.java @@ -0,0 +1,97 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; +import proguard.classfile.attribute.visitor.AllAttributeVisitor; +import proguard.classfile.constant.visitor.AllConstantVisitor; +import proguard.classfile.instruction.visitor.AllInstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.optimize.*; +import proguard.util.*; + +import java.io.*; +import java.util.*; + +/** + * This class prints out the seeds specified by keep options. + * + * @author Eric Lafortune + */ +public class SeedPrinter +{ + private final PrintStream ps; + + + /** + * Creates a new ConfigurationWriter for the given PrintStream. + */ + public SeedPrinter(PrintStream ps) throws IOException + { + this.ps = ps; + } + + + /** + * Prints out the seeds for the classes in the given program class pool. + * @param configuration the configuration containing the keep options. + * @throws IOException if an IO error occurs while writing the configuration. + */ + public void write(Configuration configuration, + 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.classesAccept(new ClassCleaner()); + libraryClassPool.classesAccept(new ClassCleaner()); + + // Create a visitor for printing out the seeds. We're printing out + // the program elements that are preserved against shrinking, + // optimization, or obfuscation. + KeepMarker keepMarker = new KeepMarker(); + ClassPoolVisitor classPoolvisitor = + ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep, + keepMarker, + keepMarker, + true, + true, + true); + // Mark the seeds. + programClassPool.accept(classPoolvisitor); + libraryClassPool.accept(classPoolvisitor); + + // Print out the seeds. + SimpleClassPrinter printer = new SimpleClassPrinter(false, ps); + programClassPool.classesAcceptAlphabetically(new MultiClassVisitor( + new ClassVisitor[] + { + new KeptClassFilter(printer), + new AllMemberVisitor(new KeptMemberFilter(printer)) + })); + } +} \ No newline at end of file diff --git a/src/proguard/SubclassedClassFilter.java b/src/proguard/SubclassedClassFilter.java new file mode 100644 index 000000000..1b9c1fee9 --- /dev/null +++ b/src/proguard/SubclassedClassFilter.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassVisitor; + +/** + * This ClassVisitor delegates all its method calls to another ClassVisitor, + * but only for Clazz objects that are being subclassed. + * + * @author Eric Lafortune + */ +final class SubclassedClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + public SubclassedClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (programClass.subClasses != null) + { + classVisitor.visitProgramClass(programClass); + } + } + + + 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 000000000..675f0df78 --- /dev/null +++ b/src/proguard/Targeter.java @@ -0,0 +1,88 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 != null ? null : new HashSet(); + + 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 000000000..7f5e7a66b --- /dev/null +++ b/src/proguard/UpToDateChecker.java @@ -0,0 +1,232 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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() + { + try + { + ModificationTimeChecker checker = new ModificationTimeChecker(); + + checker.updateInputModificationTime(configuration.lastModified); + + 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++) + { + // Update the input and output modification times. + ClassPathEntry classPathEntry = programJars.get(index); + + checker.updateModificationTime(classPathEntry.getFile(), + classPathEntry.isOutput()); + } + } + + // Check the dates of the library jars, if any. + if (libraryJars != null) + { + for (int index = 0; index < libraryJars.size(); index++) + { + // Update the input modification time. + ClassPathEntry classPathEntry = libraryJars.get(index); + + checker.updateModificationTime(classPathEntry.getFile(), + false); + } + } + + // Check the dates of the auxiliary input files. + checker.updateInputModificationTime(configuration.applyMapping); + checker.updateInputModificationTime(configuration.obfuscationDictionary); + checker.updateInputModificationTime(configuration.classObfuscationDictionary); + checker.updateInputModificationTime(configuration.packageObfuscationDictionary); + + // Check the dates of the auxiliary output files. + checker.updateOutputModificationTime(configuration.printSeeds); + checker.updateOutputModificationTime(configuration.printUsage); + checker.updateOutputModificationTime(configuration.printMapping); + checker.updateOutputModificationTime(configuration.printConfiguration); + checker.updateOutputModificationTime(configuration.dump); + } + catch (IllegalStateException e) + { + // The output is outdated. + return false; + } + + System.out.println("The output seems up to date"); + + return true; + } + + + /** + * This class maintains the modification times of input and output. + * The methods throw an IllegalStateException if the output appears + * outdated. + */ + private static class ModificationTimeChecker { + + private long inputModificationTime = Long.MIN_VALUE; + private long outputModificationTime = Long.MAX_VALUE; + + + /** + * Updates the input modification time based on the given file or + * directory (recursively). + */ + public void updateInputModificationTime(File file) + { + if (file != null) + { + updateModificationTime(file, false); + } + } + + + /** + * Updates the input modification time based on the given file or + * directory (recursively). + */ + public void updateOutputModificationTime(File file) + { + if (file != null && file.getName().length() > 0) + { + updateModificationTime(file, true); + } + } + + + /** + * Updates the specified modification time based on the given file or + * directory (recursively). + */ + public void updateModificationTime(File file, boolean isOutput) + { + // 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. + if (files.length == 0 && isOutput) + { + updateOutputModificationTime(Long.MIN_VALUE); + } + + for (int index = 0; index < files.length; index++) + { + updateModificationTime(files[index], isOutput); + } + } + else + { + // Update with the file's modification time. + updateModificationTime(file.lastModified(), isOutput); + } + } + + + /** + * Updates the specified modification time. + */ + public void updateModificationTime(long time, boolean isOutput) + { + if (isOutput) + { + updateOutputModificationTime(time); + } + else + { + updateInputModificationTime(time); + } + } + + + /** + * Updates the input modification time. + */ + public void updateInputModificationTime(long time) + { + if (inputModificationTime < time) + { + inputModificationTime = time; + + checkModificationTimes(); + } + } + + + /** + * Updates the output modification time. + */ + public void updateOutputModificationTime(long time) + { + if (outputModificationTime > time) + { + outputModificationTime = time; + + checkModificationTimes(); + } + } + + + private void checkModificationTimes() + { + if (inputModificationTime > outputModificationTime) + { + throw new IllegalStateException("The output is outdated"); + } + } + } +} diff --git a/src/proguard/WordReader.java b/src/proguard/WordReader.java new file mode 100644 index 000000000..110fce3b3 --- /dev/null +++ b/src/proguard/WordReader.java @@ -0,0 +1,387 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + + +/** + * An abstract reader of words, with the possibility to include other readers. + * Words are separated by spaces or broken off at delimiters. Words containing + * spaces or delimiters can be quoted with single or double quotes. + * Comments (everything starting with '#' on a single line) are ignored. + * + * @author Eric Lafortune + * @noinspection TailRecursion + */ +public abstract class WordReader +{ + private static final char COMMENT_CHARACTER = '#'; + + + private File baseDir; + private WordReader includeWordReader; + private String currentLine; + private int currentLineLength; + private int currentIndex; + private String currentWord; + private String currentComments; + + + /** + * Creates a new WordReader with the given base directory. + */ + protected WordReader(File baseDir) + { + this.baseDir = baseDir; + } + + + /** + * Sets the base directory of this reader. + */ + public void setBaseDir(File baseDir) + { + if (includeWordReader != null) + { + includeWordReader.setBaseDir(baseDir); + } + else + { + this.baseDir = baseDir; + } + } + + + /** + * Returns the base directory of this reader, if any. + */ + public File getBaseDir() + { + return includeWordReader != null ? + includeWordReader.getBaseDir() : + baseDir; + } + + + /** + * Specifies to start reading words from the given WordReader. When it is + * exhausted, this WordReader will continue to provide its own words. + * + * @param newIncludeWordReader the WordReader that will start reading words. + */ + public void includeWordReader(WordReader newIncludeWordReader) + { + if (includeWordReader == null) + { + includeWordReader = newIncludeWordReader; + } + else + { + includeWordReader.includeWordReader(newIncludeWordReader); + } + } + + + /** + * Reads a word from this WordReader, or from one of its active included + * WordReader objects. + * + * @param isFileName return a complete line (or argument), if the word + * isn't an option (it doesn't start with '-'). + * @return the read word. + */ + public String nextWord(boolean isFileName) throws IOException + { + currentWord = null; + + // See if we have an included reader to produce a word. + if (includeWordReader != null) + { + // Does the included word reader still produce a word? + currentWord = includeWordReader.nextWord(isFileName); + if (currentWord != null) + { + // Return it if so. + return currentWord; + } + + // Otherwise close and ditch the word reader. + includeWordReader.close(); + includeWordReader = null; + } + + // Get a word from this reader. + + // Skip any whitespace and comments left on the current line. + if (currentLine != null) + { + // Skip any leading whitespace. + while (currentIndex < currentLineLength && + Character.isWhitespace(currentLine.charAt(currentIndex))) + { + currentIndex++; + } + + // Skip any comments. + if (currentIndex < currentLineLength && + isComment(currentLine.charAt(currentIndex))) + { + currentIndex = currentLineLength; + } + } + + // Make sure we have a non-blank line. + while (currentLine == null || currentIndex == currentLineLength) + { + currentLine = nextLine(); + if (currentLine == null) + { + return null; + } + + currentLineLength = currentLine.length(); + + // Skip any leading whitespace. + currentIndex = 0; + while (currentIndex < currentLineLength && + Character.isWhitespace(currentLine.charAt(currentIndex))) + { + currentIndex++; + } + + // Remember any leading comments. + if (currentIndex < currentLineLength && + isComment(currentLine.charAt(currentIndex))) + { + // Remember the comments. + String comment = currentLine.substring(currentIndex + 1); + currentComments = currentComments == null ? + comment : + currentComments + '\n' + comment; + + // Skip the comments. + currentIndex = currentLineLength; + } + } + + // Find the word starting at the current index. + int startIndex = currentIndex; + int endIndex; + + char startChar = currentLine.charAt(startIndex); + + if (isQuote(startChar)) + { + // The next word is starting with a quote character. + // Skip the opening quote. + startIndex++; + + // The next word is a quoted character string. + // Find the closing quote. + do + { + currentIndex++; + + if (currentIndex == currentLineLength) + { + currentWord = currentLine.substring(startIndex-1, currentIndex); + throw new IOException("Missing closing quote for "+locationDescription()); + } + } + while (currentLine.charAt(currentIndex) != startChar); + + endIndex = currentIndex++; + } + else if (isFileName && + !isOption(startChar)) + { + // The next word is a (possibly optional) file name. + // Find the end of the line, the first path separator, the first + // option, or the first comment. + while (currentIndex < currentLineLength) + { + char currentCharacter = currentLine.charAt(currentIndex); + if (isFileDelimiter(currentCharacter) || + ((isOption(currentCharacter) || + isComment(currentCharacter)) && + Character.isWhitespace(currentLine.charAt(currentIndex-1)))) { + break; + } + + currentIndex++; + } + + endIndex = currentIndex; + + // Trim any trailing whitespace. + while (endIndex > startIndex && + Character.isWhitespace(currentLine.charAt(endIndex-1))) + { + endIndex--; + } + } + else if (isDelimiter(startChar)) + { + // The next word is a single delimiting character. + endIndex = ++currentIndex; + } + else + { + // The next word is a simple character string. + // Find the end of the line, the first delimiter, or the first + // white space. + while (currentIndex < currentLineLength) + { + char currentCharacter = currentLine.charAt(currentIndex); + if (isDelimiter(currentCharacter) || + Character.isWhitespace(currentCharacter) || + isComment(currentCharacter)) { + break; + } + + currentIndex++; + } + + endIndex = currentIndex; + } + + // Remember and return the parsed word. + currentWord = currentLine.substring(startIndex, endIndex); + + return currentWord; + } + + + /** + * Returns the comments collected before returning the last word. + * Starts collecting new comments. + * + * @return the collected comments, or null if there weren't any. + */ + public String lastComments() throws IOException + { + if (includeWordReader == null) + { + String comments = currentComments; + currentComments = null; + return comments; + } + else + { + return includeWordReader.lastComments(); + } + } + + + /** + * Constructs a readable description of the current position in this + * WordReader and its included WordReader objects. + * + * @return the description. + */ + public String locationDescription() + { + return + (includeWordReader == null ? + (currentWord == null ? + "end of " : + "'" + currentWord + "' in " ) : + (includeWordReader.locationDescription() + ",\n" + + " included from ")) + + lineLocationDescription(); + } + + + /** + * Reads a line from this WordReader, or from one of its active included + * WordReader objects. + * + * @return the read line. + */ + protected abstract String nextLine() throws IOException; + + + /** + * Returns a readable description of the current WordReader position. + * + * @return the description. + */ + protected abstract String lineLocationDescription(); + + + /** + * Closes the FileWordReader. + */ + public void close() throws IOException + { + // Close and ditch the included word reader, if any. + if (includeWordReader != null) + { + includeWordReader.close(); + includeWordReader = null; + } + } + + + // Small utility methods. + + private boolean isOption(char character) + { + return character == '-'; + } + + + private boolean isComment(char character) + { + return character == COMMENT_CHARACTER; + } + + + private boolean isDelimiter(char character) + { + return character == '@' || + character == '{' || + character == '}' || + character == '(' || + character == ')' || + character == ',' || + character == ';' || + character == File.pathSeparatorChar; + } + + + private boolean isFileDelimiter(char character) + { + return character == '(' || + character == ')' || + character == ',' || + character == ';' || + character == File.pathSeparatorChar; + } + + + private boolean isQuote(char character) + { + return character == '\'' || + character == '"'; + } +} diff --git a/src/proguard/ant/ClassPathElement.java b/src/proguard/ant/ClassPathElement.java new file mode 100644 index 000000000..b49612315 --- /dev/null +++ b/src/proguard/ant/ClassPathElement.java @@ -0,0 +1,191 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.ant; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.types.*; +import proguard.*; +import proguard.util.ListUtil; + +import java.io.File; + +/** + * This FileSet represents a class path entry (or a set of class path entries) + * in Ant. + * + * @author Eric Lafortune + */ +public class ClassPathElement extends Path +{ + private String filter; + private String jarFilter; + private String warFilter; + private String earFilter; + private String zipFilter; + + + /** + * @see Path#Path(Project) + */ + public ClassPathElement(Project project) + { + super(project); + } + + + /** + * Adds the contents of this class path element to the given class path. + * @param classPath the class path to be extended. + * @param output specifies whether this is an output entry or not. + */ + public void appendClassPathEntriesTo(ClassPath classPath, boolean output) + { + File baseDir = getProject().getBaseDir(); + String[] fileNames; + + if (isReference()) + { + // Get the referenced path or file set. + Object referencedObject = getCheckedRef(DataType.class, + DataType.class.getName()); + + if (referencedObject instanceof Path) + { + Path path = (Path)referencedObject; + + // Get the names of the files in the referenced path. + fileNames = path.list(); + } + else if (referencedObject instanceof AbstractFileSet) + { + AbstractFileSet fileSet = (AbstractFileSet)referencedObject; + + // Get the names of the existing input files in the referenced file set. + DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject()); + baseDir = scanner.getBasedir(); + fileNames = scanner.getIncludedFiles(); + } + else + { + throw new BuildException("The refid attribute doesn't point to a element or a element"); + } + } + else + { + // Get the names of the files in this path. + fileNames = list(); + } + + if (output) + { + if (fileNames.length != 1) + { + throw new BuildException("The element must specify exactly one file or directory ["+fileNames.length+"]"); + } + } + //else + //{ + // if (fileNames.length < 1) + // { + // throw new BuildException("The element must specify at least one file or directory"); + // } + //} + + for (int index = 0; index < fileNames.length; index++) + { + // Create a new class path entry, with the proper file name and + // any filters. + String fileName = fileNames[index]; + File file = new File(fileName); + + ClassPathEntry entry = + new ClassPathEntry(file.isAbsolute() ? file : new File(baseDir, fileName), + output); + entry.setFilter(ListUtil.commaSeparatedList(filter)); + entry.setJarFilter(ListUtil.commaSeparatedList(jarFilter)); + entry.setWarFilter(ListUtil.commaSeparatedList(warFilter)); + entry.setEarFilter(ListUtil.commaSeparatedList(earFilter)); + entry.setZipFilter(ListUtil.commaSeparatedList(zipFilter)); + + // Add it to the class path. + classPath.add(entry); + } + } + + + // Ant task attributes. + + /** + * @deprecated Use {@link #setLocation(File)} instead. + */ + public void setFile(File file) + { + setLocation(file); + } + + + /** + * @deprecated Use {@link #setLocation(File)} instead. + */ + public void setDir(File file) + { + setLocation(file); + } + + + /** + * @deprecated Use {@link #setLocation(File)} instead. + */ + public void setName(File file) + { + setLocation(file); + } + + + public void setFilter(String filter) + { + this.filter = filter; + } + + + public void setJarfilter(String jarFilter) + { + this.jarFilter = jarFilter; + } + + + public void setWarfilter(String warFilter) + { + this.warFilter = warFilter; + } + + + public void setEarfilter(String earFilter) + { + this.earFilter = earFilter; + } + + + public void setZipfilter(String zipFilter) + { + this.zipFilter = zipFilter; + } +} diff --git a/src/proguard/ant/ClassSpecificationElement.java b/src/proguard/ant/ClassSpecificationElement.java new file mode 100644 index 000000000..c07e10103 --- /dev/null +++ b/src/proguard/ant/ClassSpecificationElement.java @@ -0,0 +1,258 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.ant; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.DataType; +import proguard.*; +import proguard.classfile.ClassConstants; +import proguard.classfile.util.ClassUtil; + +import java.util.*; + +/** + * This DataType represents a class specification in Ant. + * + * @author Eric Lafortune + */ +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(); + + + /** + * Adds the contents of this class specification element to the given list. + * @param classSpecifications the class specifications to be extended. + */ + public void appendTo(List classSpecifications) + { + // Get the referenced file set, or else this one. + ClassSpecificationElement classSpecificationElement = isReference() ? + (ClassSpecificationElement)getCheckedRef(this.getClass(), + this.getClass().getName()) : + this; + + 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. + if (name != null && + name.equals(ANY_CLASS_KEYWORD)) + { + name = null; + } + + ClassSpecification classSpecification = + new ClassSpecification(null, + requiredAccessFlags(true, access, type), + requiredAccessFlags(false, access, type), + 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((MemberSpecification)fieldSpecifications.get(index)); + } + + for (int index = 0; index < methodSpecifications.size(); index++) + { + classSpecification.addMethod((MemberSpecification)methodSpecifications.get(index)); + } + + return classSpecification; + } + + + // Ant task attributes. + + public void setAccess(String access) + { + this.access = access; + } + + + public void setAnnotation(String annotation) + { + this.annotation = annotation; + } + + + public void setType(String type) + { + this.type = type; + } + + + public void setName(String name) + { + this.name = name; + } + + + public void setExtendsannotation(String extendsAnnotation) + { + this.extendsAnnotation = extendsAnnotation; + } + + + public void setExtends(String extends_) + { + this.extends_ = extends_; + } + + + public void setImplements(String implements_) + { + this.extends_ = implements_; + } + + + // Ant task nested elements. + + public void addConfiguredField(MemberSpecificationElement memberSpecificationElement) + { + if (fieldSpecifications == null) + { + fieldSpecifications = new ArrayList(); + } + + memberSpecificationElement.appendTo(fieldSpecifications, + false, + false); + } + + + public void addConfiguredMethod(MemberSpecificationElement memberSpecificationElement) + { + if (methodSpecifications == null) + { + methodSpecifications = new ArrayList(); + } + + memberSpecificationElement.appendTo(methodSpecifications, + true, + false); + } + + + public void addConfiguredConstructor(MemberSpecificationElement memberSpecificationElement) + { + if (methodSpecifications == null) + { + methodSpecifications = new ArrayList(); + } + + memberSpecificationElement.appendTo(methodSpecifications, + true, + true); + } + + + // Small utility methods. + + private int requiredAccessFlags(boolean set, + String access, + String type) + throws BuildException + { + int accessFlags = 0; + + if (access != null) + { + StringTokenizer tokenizer = new StringTokenizer(access, " ,"); + while (tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken(); + + if (token.startsWith("!") ^ set) + { + String strippedToken = token.startsWith("!") ? + token.substring(1) : + token; + + int accessFlag = + strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC) ? ClassConstants.INTERNAL_ACC_PUBLIC : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC) ? ClassConstants.INTERNAL_ACC_SYNTHETIC : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_ANNOTATION) ? ClassConstants.INTERNAL_ACC_ANNOTATTION : + 0; + + if (accessFlag == 0) + { + throw new BuildException("Incorrect class access modifier ["+strippedToken+"]"); + } + + accessFlags |= accessFlag; + } + } + } + + if (type != null && (type.startsWith("!") ^ set)) + { + int accessFlag = + type.equals("class") ? 0 : + type.equals( ClassConstants.EXTERNAL_ACC_INTERFACE) || + type.equals("!" + ClassConstants.EXTERNAL_ACC_INTERFACE) ? ClassConstants.INTERNAL_ACC_INTERFACE : + type.equals( ClassConstants.EXTERNAL_ACC_ENUM) || + type.equals("!" + ClassConstants.EXTERNAL_ACC_ENUM) ? ClassConstants.INTERNAL_ACC_ENUM : + -1; + if (accessFlag == -1) + { + throw new BuildException("Incorrect class type ["+type+"]"); + } + + accessFlags |= accessFlag; + } + + return accessFlags; + } +} diff --git a/src/proguard/ant/ConfigurationElement.java b/src/proguard/ant/ConfigurationElement.java new file mode 100644 index 000000000..d184aef41 --- /dev/null +++ b/src/proguard/ant/ConfigurationElement.java @@ -0,0 +1,124 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.ant; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.types.*; +import proguard.*; +import proguard.util.ListUtil; + +import java.io.*; +import java.util.Properties; + +/** + * This DataType represents a reference to an XML-style ProGuard configuration + * in Ant, or a file set of ProGuard-style configuration files. + * + * @author Eric Lafortune + */ +public class ConfigurationElement extends FileSet +{ + /** + * Adds the contents of this configuration element to the given + * configuration. + * @param configuration the configuration to be extended. + */ + public void appendTo(Configuration configuration) + { + File baseDir; + String[] fileNames; + + if (isReference()) + { + // Get the referenced path or file set. + Object referencedObject = getCheckedRef(Object.class, + Object.class.getName()); + + if (referencedObject instanceof ConfigurationTask) + { + // The reference doesn't point to a file set, but to a + // configuration task. + ConfigurationTask configurationTask = + (ConfigurationTask)referencedObject; + + // Append the contents of the referenced configuration to the + // current configuration. + configurationTask.appendTo(configuration); + + return; + } + else if (referencedObject instanceof AbstractFileSet) + { + AbstractFileSet fileSet = (AbstractFileSet)referencedObject; + + // Get the names of the existing input files in the referenced file set. + DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject()); + baseDir = scanner.getBasedir(); + fileNames = scanner.getIncludedFiles(); + } + else + { + throw new BuildException("The refid attribute doesn't point to a element or a element"); + } + } + else + { + // Get the names of the existing input files in the referenced file set. + DirectoryScanner scanner = getDirectoryScanner(getProject()); + baseDir = scanner.getBasedir(); + fileNames = scanner.getIncludedFiles(); + } + + // Get the combined system properties and Ant properties, for + // replacing ProGuard-style properties ('<...>'). + Properties properties = new Properties(); + properties.putAll(getProject().getProperties()); + + try + { + // Append the contents of the configuration files to the current + // configuration. + for (int index = 0; index < fileNames.length; index++) + { + File configurationFile = new File(baseDir, fileNames[index]); + + ConfigurationParser parser = + new ConfigurationParser(configurationFile, properties); + try + { + parser.parse(configuration); + } + catch (ParseException ex) + { + throw new BuildException(ex.getMessage()); + } + finally + { + parser.close(); + } + } + } + catch (IOException ex) + { + throw new BuildException(ex.getMessage()); + } + } +} diff --git a/src/proguard/ant/ConfigurationTask.java b/src/proguard/ant/ConfigurationTask.java new file mode 100644 index 000000000..376a1a14b --- /dev/null +++ b/src/proguard/ant/ConfigurationTask.java @@ -0,0 +1,450 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.ant; + +import org.apache.tools.ant.*; +import proguard.*; + +import java.io.IOException; +import java.util.*; + +/** + * This Task allows to define a ProGuard configuration from Ant. + * + * @author Eric Lafortune + */ +public class ConfigurationTask extends Task +{ + protected final Configuration configuration = new Configuration(); + + + /** + * Adds the contents of this configuration task to the given configuration. + * @param configuration the configuration to be extended. + */ + public void appendTo(Configuration configuration) + { + // Append all of these configuration entries to the given configuration. + configuration.programJars = extendClassPath(configuration.programJars, + this.configuration.programJars); + + configuration.libraryJars = extendClassPath(configuration.libraryJars, + this.configuration.libraryJars); + + configuration.keep = extendClassSpecifications(configuration.keep, + this.configuration.keep); + + configuration.keepDirectories = extendList(configuration.keepDirectories, + this.configuration.keepDirectories); + + configuration.whyAreYouKeeping = extendClassSpecifications(configuration.whyAreYouKeeping, + this.configuration.whyAreYouKeeping); + + configuration.optimizations = extendClassSpecifications(configuration.optimizations, + this.configuration.optimizations); + + configuration.assumeNoSideEffects = extendClassSpecifications(configuration.assumeNoSideEffects, + this.configuration.assumeNoSideEffects); + + configuration.keepPackageNames = extendList(configuration.keepPackageNames, + this.configuration.keepPackageNames); + + configuration.keepAttributes = extendList(configuration.keepAttributes, + this.configuration.keepAttributes); + + configuration.adaptClassStrings = extendList(configuration.adaptClassStrings, + this.configuration.adaptClassStrings); + + configuration.adaptResourceFileNames = extendList(configuration.adaptResourceFileNames, + this.configuration.adaptResourceFileNames); + + configuration.adaptResourceFileContents = extendList(configuration.adaptResourceFileContents, + this.configuration.adaptResourceFileContents); + + configuration.note = extendList(configuration.note, + this.configuration.note); + + configuration.warn = extendList(configuration.warn, + this.configuration.warn); + } + + + // Ant task nested elements. + + public void addConfiguredInjar(ClassPathElement classPathElement) + { + configuration.programJars = extendClassPath(configuration.programJars, + classPathElement, + false); + } + + + public void addConfiguredOutjar(ClassPathElement classPathElement) + { + configuration.programJars = extendClassPath(configuration.programJars, + classPathElement, + true); + } + + + public void addConfiguredLibraryjar(ClassPathElement classPathElement) + { + configuration.libraryJars = extendClassPath(configuration.libraryJars, + classPathElement, + false); + } + + + public void addConfiguredKeepdirectory(FilterElement filterElement) + { + configuration.keepDirectories = extendFilter(configuration.keepDirectories, + filterElement); + } + + + public void addConfiguredKeepdirectories(FilterElement filterElement) + { + configuration.keepDirectories = extendFilter(configuration.keepDirectories, + filterElement); + } + + + public void addConfiguredKeep(KeepSpecificationElement keepSpecificationElement) + { + configuration.keep = extendKeepSpecifications(configuration.keep, + keepSpecificationElement, + true, + false); + } + + + public void addConfiguredKeepclassmembers(KeepSpecificationElement keepSpecificationElement) + { + configuration.keep = extendKeepSpecifications(configuration.keep, + keepSpecificationElement, + false, + false); + } + + + public void addConfiguredKeepclasseswithmembers(KeepSpecificationElement keepSpecificationElement) + { + configuration.keep = extendKeepSpecifications(configuration.keep, + keepSpecificationElement, + true, + true); + } + + + public void addConfiguredKeepnames(KeepSpecificationElement keepSpecificationElement) + { + // Set the shrinking flag, based on the name (backward compatibility). + keepSpecificationElement.setAllowshrinking(true); + + configuration.keep = extendKeepSpecifications(configuration.keep, + keepSpecificationElement, + true, + false); + } + + + public void addConfiguredKeepclassmembernames(KeepSpecificationElement keepSpecificationElement) + { + // Set the shrinking flag, based on the name (backward compatibility). + keepSpecificationElement.setAllowshrinking(true); + + configuration.keep = extendKeepSpecifications(configuration.keep, + keepSpecificationElement, + false, + false); + } + + + public void addConfiguredKeepclasseswithmembernames(KeepSpecificationElement keepSpecificationElement) + { + // 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); + } + + + public void addConfiguredAssumenosideeffects(ClassSpecificationElement classSpecificationElement) + { + configuration.assumeNoSideEffects = extendClassSpecifications(configuration.assumeNoSideEffects, + classSpecificationElement); + } + + + public void addConfiguredOptimizations(FilterElement filterElement) + { + addConfiguredOptimization(filterElement); + } + + + public void addConfiguredOptimization(FilterElement filterElement) + { + configuration.optimizations = extendFilter(configuration.optimizations, + filterElement); + } + + + public void addConfiguredKeeppackagename(FilterElement filterElement) + { + configuration.keepPackageNames = extendFilter(configuration.keepPackageNames, + filterElement, + true); + } + + + public void addConfiguredKeeppackagenames(FilterElement filterElement) + { + configuration.keepPackageNames = extendFilter(configuration.keepPackageNames, + filterElement, + true); + } + + + public void addConfiguredKeepattributes(FilterElement filterElement) + { + addConfiguredKeepattribute(filterElement); + } + + + public void addConfiguredKeepattribute(FilterElement filterElement) + { + configuration.keepAttributes = extendFilter(configuration.keepAttributes, + filterElement); + } + + + public void addConfiguredAdaptclassstrings(FilterElement filterElement) + { + configuration.adaptClassStrings = extendFilter(configuration.adaptClassStrings, + filterElement, true); + } + + + public void addConfiguredAdaptresourcefilenames(FilterElement filterElement) + { + configuration.adaptResourceFileNames = extendFilter(configuration.adaptResourceFileNames, + filterElement); + } + + + public void addConfiguredAdaptresourcefilecontents(FilterElement filterElement) + { + configuration.adaptResourceFileContents = extendFilter(configuration.adaptResourceFileContents, + filterElement); + } + + + public void addConfiguredDontnote(FilterElement filterElement) + { + configuration.note = extendFilter(configuration.note, filterElement, true); + } + + + public void addConfiguredDontwarn(FilterElement filterElement) + { + configuration.warn = extendFilter(configuration.warn, filterElement, true); + } + + + public void addConfiguredConfiguration(ConfigurationElement configurationElement) + { + configurationElement.appendTo(configuration); + } + + + // Implementations for Task. + + public void addText(String text) throws BuildException + { + try + { + Project project = getProject(); + + // Replace Ant-style properties ('${...}'). + String arg = project.replaceProperties(text); + + // Get the combined system properties and Ant properties, for + // replacing ProGuard-style properties ('<...>'). + Properties properties = new Properties(); + properties.putAll(project.getProperties()); + + ConfigurationParser parser = new ConfigurationParser(arg, + "embedded configuration", + project.getBaseDir(), + properties); + + try + { + parser.parse(configuration); + } + catch (ParseException ex) + { + throw new BuildException(ex.getMessage()); + } + finally + { + parser.close(); + } + } + catch (IOException ex) + { + throw new BuildException(ex.getMessage()); + } + } + + + // Small utility methods. + + private ClassPath extendClassPath(ClassPath classPath, + ClassPathElement classPathElement, + boolean output) + { + if (classPath == null) + { + classPath = new ClassPath(); + } + + classPathElement.appendClassPathEntriesTo(classPath, + output); + + return classPath; + } + + + private ClassPath extendClassPath(ClassPath classPath, + ClassPath additionalClassPath) + { + if (additionalClassPath != null) + { + if (classPath == null) + { + classPath = new ClassPath(); + } + + classPath.addAll(additionalClassPath); + } + + return classPath; + } + + + 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) + { + if (classSpecifications == null) + { + classSpecifications = new ArrayList(); + } + + classSpecificationElement.appendTo(classSpecifications); + + return classSpecifications; + } + + + private List extendClassSpecifications(List classSpecifications, + List additionalClassSpecifications) + { + if (additionalClassSpecifications != null) + { + if (classSpecifications == null) + { + classSpecifications = new ArrayList(); + } + + classSpecifications.addAll(additionalClassSpecifications); + } + + return classSpecifications; + } + + + private List extendFilter(List filter, + FilterElement filterElement) + { + return extendFilter(filter, filterElement, false); + } + + + private List extendFilter(List filter, + FilterElement filterElement, + boolean internal) + { + if (filter == null) + { + filter = new ArrayList(); + } + + filterElement.appendTo(filter, internal); + + return filter; + } + + + private List extendList(List list, + List additionalList) + { + if (additionalList != null) + { + if (list == null) + { + list = new ArrayList(); + } + + list.addAll(additionalList); + } + + return list; + } +} diff --git a/src/proguard/ant/FilterElement.java b/src/proguard/ant/FilterElement.java new file mode 100644 index 000000000..ab3364a5d --- /dev/null +++ b/src/proguard/ant/FilterElement.java @@ -0,0 +1,85 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.ant; + +import org.apache.tools.ant.types.DataType; +import proguard.classfile.util.ClassUtil; +import proguard.util.ListUtil; + +import java.util.List; + +/** + * This DataType represents a name filter in Ant. + * + * @author Eric Lafortune + */ +public class FilterElement extends DataType +{ + private String filter; + + + /** + * Adds the contents of this element to the given name filter. + * @param filter the list of attributes to be extended. + * @param internal specifies whether the filter string should be converted + * to internal types. + */ + public void appendTo(List filter, boolean internal) + { + // Get the referenced element, or else this one. + FilterElement filterElement = isReference() ? + (FilterElement)getCheckedRef(this.getClass(), + this.getClass().getName()) : + this; + + String filterString = filterElement.filter; + + if (filterString == null) + { + // Clear the filter to keep all names. + filter.clear(); + } + else + { + if (internal) + { + filterString = ClassUtil.internalClassName(filterString); + } + + // Append the filter. + filter.addAll(ListUtil.commaSeparatedList(filterString)); + } + } + + + // Ant task attributes. + + public void setName(String name) + { + this.filter = name; + } + + + public void setFilter(String filter) + { + this.filter = filter; + } +} diff --git a/src/proguard/ant/KeepSpecificationElement.java b/src/proguard/ant/KeepSpecificationElement.java new file mode 100644 index 000000000..feabc1836 --- /dev/null +++ b/src/proguard/ant/KeepSpecificationElement.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.KeepClassSpecification; + +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; + + KeepClassSpecification keepClassSpecification = + new KeepClassSpecification(markClasses, + markConditionally, + allowShrinking, + allowOptimization, + allowObfuscation, + createClassSpecification(keepSpecificationElement)); + + // Add it to the list. + keepSpecifications.add(keepClassSpecification); + } + + + // 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/MemberSpecificationElement.java b/src/proguard/ant/MemberSpecificationElement.java new file mode 100644 index 000000000..976200985 --- /dev/null +++ b/src/proguard/ant/MemberSpecificationElement.java @@ -0,0 +1,218 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.ant; + +import org.apache.tools.ant.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.*; + +/** + * This DataType represents a class member specification in Ant. + * + * @author Eric Lafortune + */ +public class MemberSpecificationElement extends DataType +{ + private String access; + private String annotation; + private String type; + private String name; + private String parameters; + + + /** + * Adds the contents of this class member specification element to the given + * list. + * @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 memberSpecifications, + boolean isMethod, + boolean isConstructor) + { + // Get the referenced file set, or else this one. + MemberSpecificationElement memberSpecificationElement = isReference() ? + (MemberSpecificationElement)getCheckedRef(this.getClass(), + this.getClass().getName()) : + this; + + // Create a new class member specification. + 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); + } + + if (isMethod) + { + if (isConstructor) + { + if (type != null) + { + throw new BuildException("Type attribute not allowed in constructor specification ["+type+"]"); + } + + if (parameters != null) + { + type = ClassConstants.EXTERNAL_TYPE_VOID; + } + + name = ClassConstants.INTERNAL_METHOD_NAME_INIT; + } + else if ((type != null) ^ (parameters != null)) + { + throw new BuildException("Type and parameters attributes must always be present in combination in method specification"); + } + } + else + { + if (parameters != null) + { + throw new BuildException("Parameters attribute not allowed in field specification ["+parameters+"]"); + } + } + + List parameterList = ListUtil.commaSeparatedList(parameters); + + String descriptor = + parameters != null ? ClassUtil.internalMethodDescriptor(type, parameterList) : + type != null ? ClassUtil.internalType(type) : + null; + + MemberSpecification memberSpecification = + new MemberSpecification(requiredAccessFlags(true, access), + requiredAccessFlags(false, access), + annotation, + name, + descriptor); + + // Add it to the list. + memberSpecifications.add(memberSpecification); + } + + + // Ant task attributes. + + public void setAccess(String access) + { + this.access = access; + } + + + public void setAnnotation(String annotation) + { + this.annotation = annotation; + } + + + public void setType(String type) + { + this.type = type; + } + + + public void setName(String name) + { + this.name = name; + } + + + public void setParameters(String parameters) + { + this.parameters = parameters; + } + + + /** + * @deprecated Use {@link #setParameters(String)} instead. + */ + public void setParam(String parameters) + { + this.parameters = parameters; + } + + + // Small utility methods. + + private int requiredAccessFlags(boolean set, + String access) + throws BuildException + { + int accessFlags = 0; + + if (access != null) + { + StringTokenizer tokenizer = new StringTokenizer(access, " ,"); + while (tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken(); + + if (token.startsWith("!") ^ set) + { + String strippedToken = token.startsWith("!") ? + token.substring(1) : + token; + + int accessFlag = + strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC) ? ClassConstants.INTERNAL_ACC_PUBLIC : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_PRIVATE) ? ClassConstants.INTERNAL_ACC_PRIVATE : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_PROTECTED) ? ClassConstants.INTERNAL_ACC_PROTECTED : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_STATIC) ? ClassConstants.INTERNAL_ACC_STATIC : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED) ? ClassConstants.INTERNAL_ACC_SYNCHRONIZED : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_VOLATILE) ? ClassConstants.INTERNAL_ACC_VOLATILE : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_TRANSIENT) ? ClassConstants.INTERNAL_ACC_TRANSIENT : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_BRIDGE) ? ClassConstants.INTERNAL_ACC_BRIDGE : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_VARARGS) ? ClassConstants.INTERNAL_ACC_VARARGS : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_NATIVE) ? ClassConstants.INTERNAL_ACC_NATIVE : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_STRICT) ? ClassConstants.INTERNAL_ACC_STRICT : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC) ? ClassConstants.INTERNAL_ACC_SYNTHETIC : + 0; + + if (accessFlag == 0) + { + throw new BuildException("Incorrect class member access modifier ["+strippedToken+"]"); + } + + accessFlags |= accessFlag; + } + } + } + + return accessFlags; + } +} diff --git a/src/proguard/ant/ProGuardTask.java b/src/proguard/ant/ProGuardTask.java new file mode 100644 index 000000000..7ab1cdf07 --- /dev/null +++ b/src/proguard/ant/ProGuardTask.java @@ -0,0 +1,357 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.ant; + +import org.apache.tools.ant.BuildException; +import proguard.*; +import proguard.classfile.util.ClassUtil; + +import java.io.*; +import java.util.*; + +/** + * This Task allows to configure and run ProGuard from Ant. + * + * @author Eric Lafortune + */ +public class ProGuardTask extends ConfigurationTask +{ + // Ant task attributes. + + public void setConfiguration(File configurationFile) throws BuildException + { + try + { + // Get the combined system properties and Ant properties, for + // replacing ProGuard-style properties ('<...>'). + Properties properties = new Properties(); + properties.putAll(getProject().getProperties()); + + ConfigurationParser parser = new ConfigurationParser(configurationFile, + properties); + try + { + parser.parse(configuration); + } + catch (ParseException ex) + { + throw new BuildException(ex.getMessage()); + } + finally + { + parser.close(); + } + } + catch (IOException ex) + { + throw new BuildException(ex.getMessage()); + } + } + + + /** + * @deprecated Use the nested outjar element instead. + */ + public void setOutjar(String parameters) + { + throw new BuildException("Use the nested element instead of the 'outjar' attribute"); + } + + + public void setSkipnonpubliclibraryclasses(boolean skipNonPublicLibraryClasses) + { + configuration.skipNonPublicLibraryClasses = skipNonPublicLibraryClasses; + } + + + public void setSkipnonpubliclibraryclassmembers(boolean skipNonPublicLibraryClassMembers) + { + configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembers; + } + + + 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); + } + + + public void setShrink(boolean shrink) + { + configuration.shrink = shrink; + } + + + public void setPrintusage(File printUsage) + { + configuration.printUsage = optionalFile(printUsage); + } + + + public void setOptimize(boolean optimize) + { + configuration.optimize = optimize; + } + + + public void setOptimizationpasses(int optimizationPasses) + { + configuration.optimizationPasses = optimizationPasses; + } + + + public void setAllowaccessmodification(boolean allowAccessModification) + { + configuration.allowAccessModification = allowAccessModification; + } + + + public void setMergeinterfacesaggressively(boolean mergeinterfacesaggressively) + { + configuration.mergeInterfacesAggressively = mergeinterfacesaggressively; + } + + + public void setObfuscate(boolean obfuscate) + { + configuration.obfuscate = obfuscate; + } + + + public void setPrintmapping(File printMapping) + { + configuration.printMapping = optionalFile(printMapping); + } + + + public void setApplymapping(File applyMapping) + { + configuration.applyMapping = resolvedFile(applyMapping); + } + + + public void setObfuscationdictionary(File obfuscationDictionary) + { + configuration.obfuscationDictionary = resolvedFile(obfuscationDictionary); + } + + + public void setClassobfuscationdictionary(File classObfuscationDictionary) + { + configuration.classObfuscationDictionary = resolvedFile(classObfuscationDictionary); + } + + + public void setPackageobfuscationdictionary(File packageObfuscationDictionary) + { + configuration.packageObfuscationDictionary = resolvedFile(packageObfuscationDictionary); + } + + + public void setOverloadaggressively(boolean overloadAggressively) + { + configuration.overloadAggressively = overloadAggressively; + } + + + public void setUseuniqueclassmembernames(boolean useUniqueClassMemberNames) + { + configuration.useUniqueClassMemberNames = useUniqueClassMemberNames; + } + + + public void setUsemixedcaseclassnames(boolean useMixedCaseClassNames) + { + configuration.useMixedCaseClassNames = useMixedCaseClassNames; + } + + + public void setFlattenpackagehierarchy(String flattenPackageHierarchy) + { + 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); + } + + + public void setKeepparameternames(boolean keepParameterNames) + { + configuration.keepParameterNames = keepParameterNames; + } + + + public void setRenamesourcefileattribute(String newSourceFileAttribute) + { + configuration.newSourceFileAttribute = newSourceFileAttribute; + } + + + public void setPreverify(boolean preverify) + { + configuration.preverify = preverify; + } + + + public void setMicroedition(boolean microEdition) + { + configuration.microEdition = microEdition; + } + + + public void setVerbose(boolean verbose) + { + configuration.verbose = verbose; + } + + + public void setNote(boolean note) + { + if (note) + { + // Switch on notes if they were completely disabled. + if (configuration.note != null && + configuration.note.isEmpty()) + { + configuration.note = null; + } + } + else + { + // Switch off notes. + configuration.note = new ArrayList(); + } + } + + + public void setWarn(boolean warn) + { + if (warn) + { + // Switch on warnings if they were completely disabled. + if (configuration.warn != null && + configuration.warn.isEmpty()) + { + configuration.warn = null; + } + } + else + { + // Switch off warnings. + configuration.warn = new ArrayList(); + } + } + + + public void setIgnorewarnings(boolean ignoreWarnings) + { + configuration.ignoreWarnings = ignoreWarnings; + } + + + public void setPrintconfiguration(File printConfiguration) + { + configuration.printConfiguration = optionalFile(printConfiguration); + } + + + public void setDump(File dump) + { + configuration.dump = optionalFile(dump); + } + + + // Implementations for Task. + + public void execute() throws BuildException + { + try + { + ProGuard proGuard = new ProGuard(configuration); + proGuard.execute(); + } + catch (IOException ex) + { + throw new BuildException(ex.getMessage()); + } + } + + + // Small utility methods. + + /** + * Returns a file that is properly resolved with respect to the project + * directory, or null or empty if its name is actually a + * boolean flag. + */ + private File optionalFile(File file) + { + String fileName = file.getName(); + + return + fileName.equalsIgnoreCase("false") || + fileName.equalsIgnoreCase("no") || + fileName.equalsIgnoreCase("off") ? null : + fileName.equalsIgnoreCase("true") || + fileName.equalsIgnoreCase("yes") || + fileName.equalsIgnoreCase("on") ? Configuration.STD_OUT : + resolvedFile(file); + } + + + /** + * Returns a file that is properly resolved with respect to the project + * directory. + */ + private File resolvedFile(File file) + { + return file.isAbsolute() ? file : + new File(getProject().getBaseDir(), + file.getName()); + } +} diff --git a/src/proguard/ant/package.html b/src/proguard/ant/package.html new file mode 100644 index 000000000..75e0466df --- /dev/null +++ b/src/proguard/ant/package.html @@ -0,0 +1,3 @@ + +This package contains the Ant task for ProGuard. + diff --git a/src/proguard/ant/task.properties b/src/proguard/ant/task.properties new file mode 100644 index 000000000..82d0f3f41 --- /dev/null +++ b/src/proguard/ant/task.properties @@ -0,0 +1,2 @@ +proguard = proguard.ant.ProGuardTask +proguardconfiguration = proguard.ant.ConfigurationTask diff --git a/src/proguard/classfile/ClassConstants.java b/src/proguard/classfile/ClassConstants.java new file mode 100644 index 000000000..59580c543 --- /dev/null +++ b/src/proguard/classfile/ClassConstants.java @@ -0,0 +1,329 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * Constants used in representing a Java class (*.class). + * + * @author Eric Lafortune + */ +public interface ClassConstants +{ + public static final String CLASS_FILE_EXTENSION = ".class"; + + public static final int MAGIC = 0xCAFEBABE; + + 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_7_MAJOR = 51; + public static final int INTERNAL_CLASS_VERSION_1_7_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 int INTERNAL_CLASS_VERSION_1_7 = (INTERNAL_CLASS_VERSION_1_7_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_7_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_7 = "1.7"; + 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 String EXTERNAL_CLASS_VERSION_1_7_ALIAS = "7"; + + public static final int INTERNAL_ACC_PUBLIC = 0x0001; + public static final int INTERNAL_ACC_PRIVATE = 0x0002; + public static final int INTERNAL_ACC_PROTECTED = 0x0004; + public static final int INTERNAL_ACC_STATIC = 0x0008; + public static final int INTERNAL_ACC_FINAL = 0x0010; + public static final int INTERNAL_ACC_SUPER = 0x0020; + public static final int INTERNAL_ACC_SYNCHRONIZED = 0x0020; + public static final int INTERNAL_ACC_VOLATILE = 0x0040; + public static final int INTERNAL_ACC_TRANSIENT = 0x0080; + public static final int INTERNAL_ACC_BRIDGE = 0x0040; + public static final int INTERNAL_ACC_VARARGS = 0x0080; + public static final int INTERNAL_ACC_NATIVE = 0x0100; + public static final int INTERNAL_ACC_INTERFACE = 0x0200; + public static final int INTERNAL_ACC_ABSTRACT = 0x0400; + public static final int INTERNAL_ACC_STRICT = 0x0800; + public static final int INTERNAL_ACC_SYNTHETIC = 0x1000; + public static final int INTERNAL_ACC_ANNOTATTION = 0x2000; + public static final int INTERNAL_ACC_ENUM = 0x4000; + + public static final int VALID_INTERNAL_ACC_CLASS = INTERNAL_ACC_PUBLIC | + INTERNAL_ACC_FINAL | + INTERNAL_ACC_SUPER | + INTERNAL_ACC_INTERFACE | + INTERNAL_ACC_ABSTRACT | + INTERNAL_ACC_SYNTHETIC | + INTERNAL_ACC_ANNOTATTION | + INTERNAL_ACC_ENUM; + public static final int VALID_INTERNAL_ACC_FIELD = INTERNAL_ACC_PUBLIC | + INTERNAL_ACC_PRIVATE | + INTERNAL_ACC_PROTECTED | + INTERNAL_ACC_STATIC | + INTERNAL_ACC_FINAL | + INTERNAL_ACC_VOLATILE | + INTERNAL_ACC_TRANSIENT | + INTERNAL_ACC_SYNTHETIC | + INTERNAL_ACC_ENUM; + public static final int VALID_INTERNAL_ACC_METHOD = INTERNAL_ACC_PUBLIC | + INTERNAL_ACC_PRIVATE | + INTERNAL_ACC_PROTECTED | + INTERNAL_ACC_STATIC | + INTERNAL_ACC_FINAL | + INTERNAL_ACC_SYNCHRONIZED | + INTERNAL_ACC_BRIDGE | + INTERNAL_ACC_VARARGS | + INTERNAL_ACC_NATIVE | + INTERNAL_ACC_ABSTRACT | + INTERNAL_ACC_STRICT | + INTERNAL_ACC_SYNTHETIC; + + public static final String EXTERNAL_ACC_PUBLIC = "public"; + public static final String EXTERNAL_ACC_PRIVATE = "private"; + public static final String EXTERNAL_ACC_PROTECTED = "protected"; + public static final String EXTERNAL_ACC_STATIC = "static"; + public static final String EXTERNAL_ACC_FINAL = "final"; + public static final String EXTERNAL_ACC_SUPER = "super"; + public static final String EXTERNAL_ACC_SYNCHRONIZED = "synchronized"; + public static final String EXTERNAL_ACC_VOLATILE = "volatile"; + public static final String EXTERNAL_ACC_TRANSIENT = "transient"; + public static final String EXTERNAL_ACC_BRIDGE = "bridge"; + public static final String EXTERNAL_ACC_VARARGS = "varargs"; + public static final String EXTERNAL_ACC_NATIVE = "native"; + public static final String EXTERNAL_ACC_INTERFACE = "interface"; + public static final String EXTERNAL_ACC_ABSTRACT = "abstract"; + public static final String EXTERNAL_ACC_STRICT = "strictfp"; + public static final String EXTERNAL_ACC_SYNTHETIC = "synthetic"; + public static final String EXTERNAL_ACC_ANNOTATION = "@"; + public static final String EXTERNAL_ACC_ENUM = "enum"; + + public static final int CONSTANT_Utf8 = 1; + public static final int CONSTANT_Integer = 3; + public static final int CONSTANT_Float = 4; + public static final int CONSTANT_Long = 5; + public static final int CONSTANT_Double = 6; + public static final int CONSTANT_Class = 7; + public static final int CONSTANT_String = 8; + public static final int CONSTANT_Fieldref = 9; + public static final int CONSTANT_Methodref = 10; + public static final int CONSTANT_InterfaceMethodref = 11; + public static final int CONSTANT_NameAndType = 12; + public static final int CONSTANT_MethodHandle = 15; + public static final int CONSTANT_MethodType = 16; + public static final int CONSTANT_InvokeDynamic = 18; + + public static final int REF_getField = 1; + public static final int REF_getStatic = 2; + public static final int REF_putField = 3; + public static final int REF_putStatic = 4; + public static final int REF_invokeVirtual = 5; + public static final int REF_invokeStatic = 6; + public static final int REF_invokeSpecial = 7; + public static final int REF_newInvokeSpecial = 8; + public static final int REF_invokeInterface = 9; + + public static final String ATTR_BootstrapMethods = "BootstrapMethods"; + 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_RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations"; + public static final String ATTR_RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations"; + public static final String ATTR_RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations"; + public static final String ATTR_RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations"; + public static final String ATTR_AnnotationDefault = "AnnotationDefault"; + + public static final char ELEMENT_VALUE_STRING_CONSTANT = 's'; + public static final char ELEMENT_VALUE_ENUM_CONSTANT = 'e'; + public static final char ELEMENT_VALUE_CLASS = 'c'; + public static final char ELEMENT_VALUE_ANNOTATION = '@'; + public static final char ELEMENT_VALUE_ARRAY = '['; + + public static final char EXTERNAL_PACKAGE_SEPARATOR = '.'; + public static final char EXTERNAL_INNER_CLASS_SEPARATOR = '.'; + public static final char INTERNAL_PACKAGE_SEPARATOR = '/'; + public static final char INTERNAL_INNER_CLASS_SEPARATOR = '$'; + public static final char SPECIAL_CLASS_CHARACTER = '-'; + public static final char SPECIAL_MEMBER_SEPARATOR = '$'; + + public static final char EXTERNAL_METHOD_ARGUMENTS_OPEN = '('; + public static final char EXTERNAL_METHOD_ARGUMENTS_CLOSE = ')'; + 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 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_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_LANG_STRING_BUFFER = "java/lang/StringBuffer"; + public static final String INTERNAL_NAME_JAVA_LANG_STRING_BUILDER = "java/lang/StringBuilder"; + public static final String INTERNAL_NAME_JAVA_LANG_INVOKE_METHOD_HANDLE = "java/lang/invoke/MethodHandle"; + public static final String INTERNAL_NAME_JAVA_LANG_INVOKE_METHOD_TYPE = "java/lang/invoke/MethodType"; + public static final String INTERNAL_NAME_JAVA_IO_SERIALIZABLE = "java/io/Serializable"; + + public static final String INTERNAL_NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicIntegerFieldUpdater"; + public static final String INTERNAL_NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicLongFieldUpdater"; + public static final String INTERNAL_NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicReferenceFieldUpdater"; + + public static final String INTERNAL_METHOD_NAME_INIT = ""; + public static final String INTERNAL_METHOD_TYPE_INIT = "()V"; + public static final String INTERNAL_METHOD_NAME_CLINIT = ""; + public static final String INTERNAL_METHOD_TYPE_CLINIT = "()V"; + + 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_CONSTRUCTOR_NAME_CLASS_GET_CONSTRUCTOR = "getConstructor"; + public static final String INTERNAL_CONSTRUCTOR_TYPE_CLASS_GET_CONSTRUCTOR = "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"; + public static final String INTERNAL_CONSTRUCTOR_NAME_CLASS_GET_DECLARED_CONSTRUCTOR = "getDeclaredConstructor"; + public static final String INTERNAL_CONSTRUCTOR_TYPE_CLASS_GET_DECLARED_CONSTRUCTOR = "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"; + 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_NEW_UPDATER = "newUpdater"; + public static final String INTERNAL_METHOD_TYPE_NEW_INTEGER_UPDATER = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater;"; + public static final String INTERNAL_METHOD_TYPE_NEW_LONG_UPDATER = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicLongFieldUpdater;"; + public static final String INTERNAL_METHOD_TYPE_NEW_REFERENCE_UPDATER = "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;"; + + 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_TYPE_INIT_ENUM = "(Ljava/lang/String;I)V"; + + 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 String INTERNAL_METHOD_NAME_EQUALS = "equals"; + public static final String INTERNAL_METHOD_TYPE_EQUALS = "(Ljava/lang/Object;)Z"; + public static final String INTERNAL_METHOD_NAME_LENGTH = "length"; + public static final String INTERNAL_METHOD_NAME_VALUEOF = "valueOf"; + public static final String INTERNAL_METHOD_TYPE_VALUEOF_BOOLEAN = "(Z)Ljava/lang/String;"; + public static final String INTERNAL_METHOD_TYPE_VALUEOF_CHAR = "(C)Ljava/lang/String;"; + public static final String INTERNAL_METHOD_TYPE_VALUEOF_INT = "(I)Ljava/lang/String;"; + public static final String INTERNAL_METHOD_TYPE_VALUEOF_LONG = "(J)Ljava/lang/String;"; + public static final String INTERNAL_METHOD_TYPE_VALUEOF_FLOAT = "(F)Ljava/lang/String;"; + public static final String INTERNAL_METHOD_TYPE_VALUEOF_DOUBLE = "(D)Ljava/lang/String;"; + public static final String INTERNAL_METHOD_TYPE_VALUEOF_OBJECT = "(Ljava/lang/Object;)Ljava/lang/String;"; + + public static final String INTERNAL_METHOD_TYPE_LENGTH = "()I"; + public static final String INTERNAL_METHOD_NAME_APPEND = "append"; + public static final String INTERNAL_METHOD_TYPE_STRING_VOID = "(Ljava/lang/String;)V"; + public static final String INTERNAL_METHOD_TYPE_BOOLEAN_STRING_BUFFER = "(Z)Ljava/lang/StringBuffer;"; + public static final String INTERNAL_METHOD_TYPE_CHAR_STRING_BUFFER = "(C)Ljava/lang/StringBuffer;"; + public static final String INTERNAL_METHOD_TYPE_INT_STRING_BUFFER = "(I)Ljava/lang/StringBuffer;"; + public static final String INTERNAL_METHOD_TYPE_LONG_STRING_BUFFER = "(J)Ljava/lang/StringBuffer;"; + public static final String INTERNAL_METHOD_TYPE_FLOAT_STRING_BUFFER = "(F)Ljava/lang/StringBuffer;"; + public static final String INTERNAL_METHOD_TYPE_DOUBLE_STRING_BUFFER = "(D)Ljava/lang/StringBuffer;"; + public static final String INTERNAL_METHOD_TYPE_STRING_STRING_BUFFER = "(Ljava/lang/String;)Ljava/lang/StringBuffer;"; + public static final String INTERNAL_METHOD_TYPE_OBJECT_STRING_BUFFER = "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"; + public static final String INTERNAL_METHOD_TYPE_BOOLEAN_STRING_BUILDER = "(Z)Ljava/lang/StringBuilder;"; + public static final String INTERNAL_METHOD_TYPE_CHAR_STRING_BUILDER = "(C)Ljava/lang/StringBuilder;"; + public static final String INTERNAL_METHOD_TYPE_INT_STRING_BUILDER = "(I)Ljava/lang/StringBuilder;"; + public static final String INTERNAL_METHOD_TYPE_LONG_STRING_BUILDER = "(J)Ljava/lang/StringBuilder;"; + public static final String INTERNAL_METHOD_TYPE_FLOAT_STRING_BUILDER = "(F)Ljava/lang/StringBuilder;"; + public static final String INTERNAL_METHOD_TYPE_DOUBLE_STRING_BUILDER = "(D)Ljava/lang/StringBuilder;"; + public static final String INTERNAL_METHOD_TYPE_STRING_STRING_BUILDER = "(Ljava/lang/String;)Ljava/lang/StringBuilder;"; + public static final String INTERNAL_METHOD_TYPE_OBJECT_STRING_BUILDER = "(Ljava/lang/Object;)Ljava/lang/StringBuilder;"; + public static final String INTERNAL_METHOD_NAME_TOSTRING = "toString"; + public static final String INTERNAL_METHOD_TYPE_TOSTRING = "()Ljava/lang/String;"; + + public static final char INTERNAL_TYPE_VOID = 'V'; + public static final char INTERNAL_TYPE_BOOLEAN = 'Z'; + public static final char INTERNAL_TYPE_BYTE = 'B'; + public static final char INTERNAL_TYPE_CHAR = 'C'; + public static final char INTERNAL_TYPE_SHORT = 'S'; + public static final char INTERNAL_TYPE_INT = 'I'; + public static final char INTERNAL_TYPE_LONG = 'J'; + public static final char INTERNAL_TYPE_FLOAT = 'F'; + public static final char INTERNAL_TYPE_DOUBLE = 'D'; + public static final char INTERNAL_TYPE_CLASS_START = 'L'; + public static final char INTERNAL_TYPE_CLASS_END = ';'; + public static final char INTERNAL_TYPE_ARRAY = '['; + public static final char INTERNAL_TYPE_GENERIC_VARIABLE_START = 'T'; + public static final char INTERNAL_TYPE_GENERIC_START = '<'; + public static final char INTERNAL_TYPE_GENERIC_BOUND = ':'; + public static final char INTERNAL_TYPE_GENERIC_END = '>'; + + public static final String EXTERNAL_TYPE_JAVA_LANG_OBJECT = "java.lang.Object"; + public static final String EXTERNAL_PACKAGE_JAVA_LANG = "java.lang."; + + 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/ClassPool.java b/src/proguard/classfile/ClassPool.java new file mode 100644 index 000000000..5439c6f06 --- /dev/null +++ b/src/proguard/classfile/ClassPool.java @@ -0,0 +1,161 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.util.ClassUtil; +import proguard.classfile.visitor.*; + +import java.util.*; + +/** + * 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 +{ + // We're using a sorted tree map instead of a hash map to store the classes, + // in order to make the processing more deterministic. + private final Map classes = new TreeMap(); + + + /** + * Clears the class pool. + */ + public void clear() + { + classes.clear(); + } + + + /** + * Adds the given Clazz to the class pool. + */ + public void addClass(Clazz clazz) + { + classes.put(clazz.getName(), clazz); + } + + + /** + * Removes the given Clazz from the class pool. + */ + public void removeClass(Clazz clazz) + { + removeClass(clazz.getName()); + } + + + /** + * Removes the specified Clazz from the class pool. + */ + public void removeClass(String className) + { + classes.remove(className); + } + + + /** + * Returns a Clazz from the class pool based on its name. Returns + * null if the class with the given name is not in the class + * pool. + */ + public Clazz getClass(String className) + { + return (Clazz)classes.get(className); + } + + + /** + * Returns an Iterator of all class names in the class pool. + */ + public Iterator classNames() + { + return classes.keySet().iterator(); + } + + + /** + * Returns the number of classes in the class pool. + */ + public int size() + { + return classes.size(); + } + + + /** + * Applies the given ClassPoolVisitor to the class pool. + */ + public void accept(ClassPoolVisitor classPoolVisitor) + { + classPoolVisitor.visitClassPool(this); + } + + + /** + * Applies the given ClassVisitor to all classes in the class pool, + * in random order. + */ + public void classesAccept(ClassVisitor classVisitor) + { + Iterator iterator = classes.values().iterator(); + while (iterator.hasNext()) + { + Clazz clazz = (Clazz)iterator.next(); + clazz.accept(classVisitor); + } + } + + + /** + * Applies the given ClassVisitor to all classes in the class pool, + * in sorted order. + */ + public void classesAcceptAlphabetically(ClassVisitor classVisitor) + { + // We're already using a tree map. + //TreeMap sortedClasses = new TreeMap(classes); + //Iterator iterator = sortedClasses.values().iterator(); + + Iterator iterator = classes.values().iterator(); + while (iterator.hasNext()) + { + Clazz clazz = (Clazz)iterator.next(); + clazz.accept(classVisitor); + } + } + + + /** + * Applies the given ClassVisitor to the class with the given name, + * if it is present in the class pool. + */ + public void classAccept(String className, ClassVisitor classVisitor) + { + Clazz clazz = getClass(className); + if (clazz != null) + { + clazz.accept(classVisitor); + } + } +} diff --git a/src/proguard/classfile/Clazz.java b/src/proguard/classfile/Clazz.java new file mode 100644 index 000000000..35f8a1c2d --- /dev/null +++ b/src/proguard/classfile/Clazz.java @@ -0,0 +1,261 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.*; + + +/** + * This interface provides access to the representation of a Java class. + * + * @author Eric Lafortune + */ +public interface Clazz extends VisitorAccepter +{ + /** + * Returns the access flags of this class. + * @see ClassConstants + */ + public int getAccessFlags(); + + /** + * Returns the full internal name of this class. + */ + public String getName(); + + /** + * Returns the full internal name of the super class of this class, or + * null if this class represents java.lang.Object. + */ + public String getSuperName(); + + /** + * Returns the number of interfaces that this class implements. + */ + public int getInterfaceCount(); + + /** + * Returns the full internal name of the interface at the given index of + * this class. + */ + public String getInterfaceName(int 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 String getString(int constantIndex); + + /** + * Returns the String value of the StringConstant at the specified index. + */ + public String getStringString(int constantIndex); + + /** + * Returns the class name of ClassConstant at the specified index. + */ + public String getClassName(int constantIndex); + + /** + * Returns the name of the NameAndTypeConstant at the specified index. + */ + public String getName(int constantIndex); + + /** + * Returns the type of the NameAndTypeConstant at the specified index. + */ + public String getType(int constantIndex); + + /** + * Returns the name of the RefConstant at the specified index. + */ + public String getRefName(int constantIndex); + + /** + * Returns the type of the RefConstant at the specified index. + */ + public String getRefType(int constantIndex); + + + // Methods pertaining to related classes. + + /** + * Notifies this Clazz that it is being subclassed by another class. + */ + public void addSubClass(Clazz clazz); + + /** + * Returns the super class of this class. + */ + public Clazz getSuperClass(); + + /** + * Returns the interface at the given 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_(Clazz clazz); + + /** + * Returns whether this class extends the specified class. + * A class is always considered to extend itself. + * Interfaces are considered to only extend the root Object class. + */ + public boolean extends_(String className); + + /** + * 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 extendsOrImplements(Clazz clazz); + + /** + * Returns whether this class implements the specified class. + * A class is always considered to implement itself. + * Interfaces are considered to implement all their superinterfaces. + */ + public boolean extendsOrImplements(String className); + + + // Methods for getting specific class members. + + /** + * Returns the field with the given name and descriptor. + */ + Field findField(String name, String descriptor); + + /** + * Returns the method with the given name and descriptor. + */ + Method findMethod(String name, String descriptor); + + + // Methods for accepting various types of visitors. + + /** + * Accepts the given class visitor. + */ + public void accept(ClassVisitor classVisitor); + + /** + * 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 classVisitor the ClassVisitor that will + * visit the class hierarchy. + */ + public void hierarchyAccept(boolean visitThisClass, + boolean visitSuperClass, + boolean visitInterfaces, + boolean visitSubclasses, + ClassVisitor classVisitor); + + /** + * Lets the given class visitor visit all known subclasses. + * @param classVisitor the ClassVisitor that will visit the + * subclasses. + */ + public void subclassesAccept(ClassVisitor classVisitor); + + /** + * Lets the given constant pool entry visitor visit all constant pool entries + * of this class. + */ + 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, ConstantVisitor constantVisitor); + + /** + * Lets the given constant pool entry visitor visit the class constant pool + * entry of this class. + */ + public void thisClassConstantAccept(ConstantVisitor constantVisitor); + + /** + * Lets the given constant pool entry visitor visit the class constant pool + * entry of the super class of this class, if there is one. + */ + public void superClassConstantAccept(ConstantVisitor constantVisitor); + + /** + * Lets the given constant pool entry visitor visit the class constant pool + * entries for all interfaces of this class. + */ + public void interfaceConstantsAccept(ConstantVisitor constantVisitor); + + /** + * Lets the given member info visitor visit all fields of this class. + */ + public void fieldsAccept(MemberVisitor memberVisitor); + + /** + * Lets the given member info visitor visit the specified field. + */ + public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor); + + /** + * Lets the given member info visitor visit all methods of this class. + */ + public void methodsAccept(MemberVisitor memberVisitor); + + /** + * Lets the given member info visitor visit the specified method. + */ + 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 method the method that may have implementations. + * @return whether it may have implementations. + */ + public boolean mayHaveImplementations(Method method); + + /** + * Lets the given attribute info visitor visit all attributes of this class. + */ + public void attributesAccept(AttributeVisitor attributeVisitor); + + /** + * Lets the given attribute info visitor visit the specified attribute. + */ + public void attributeAccept(String name, AttributeVisitor attributeVisitor); +} diff --git a/src/proguard/classfile/Field.java b/src/proguard/classfile/Field.java new file mode 100644 index 000000000..61bf2daee --- /dev/null +++ b/src/proguard/classfile/Field.java @@ -0,0 +1,32 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + + +/** + * Representation of a field from a class. + * + * @author Eric Lafortune + */ +public interface Field extends Member +{ +} diff --git a/src/proguard/classfile/LibraryClass.java b/src/proguard/classfile/LibraryClass.java new file mode 100644 index 000000000..151a32a09 --- /dev/null +++ b/src/proguard/classfile/LibraryClass.java @@ -0,0 +1,548 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 String getRefName(int constantIndex) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool"); + } + + public String getRefType(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 extends_(String className) + { + if (getName().equals(className)) + { + return true; + } + + return superClass != null && + superClass.extends_(className); + } + + + 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 boolean extendsOrImplements(String className) + { + if (getName().equals(className)) + { + return true; + } + + if (superClass != null && + superClass.extendsOrImplements(className)) + { + return true; + } + + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + Clazz interfaceClass = interfaceClasses[index]; + if (interfaceClass != null && + interfaceClass.extendsOrImplements(className)) + { + 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) + { + // Visit the interfaces of the superclasses, if we haven't done so yet. + if (!visitSuperClass) + { + if (superClass != null) + { + superClass.hierarchyAccept(false, + false, + true, + false, + classVisitor); + } + } + + // Visit the interfaces. + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + Clazz interfaceClass = interfaceClasses[index]; + if (interfaceClass != null) + { + interfaceClass.hierarchyAccept(true, + false, + 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); + } + } + } + } + + + /** + * Lets the given class visitor visit the superclass, if it is known. + * @param classVisitor the ClassVisitor that will visit the + * superclass. + */ + public void superClassAccept(ClassVisitor classVisitor) + { + if (superClass != null) + { + superClass.accept(classVisitor); + } + } + + + /** + * Lets the given class visitor visit all known direct interfaces. + * @param classVisitor the ClassVisitor that will visit the + * interfaces. + */ + public void interfacesAccept(ClassVisitor classVisitor) + { + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + Clazz interfaceClass = interfaceClasses[index]; + if (interfaceClass != null) + { + interfaceClass.accept(classVisitor); + } + } + } + } + + + public void subclassesAccept(ClassVisitor classVisitor) + { + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].accept(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 thisClassConstantAccept(ConstantVisitor constantVisitor) + { + // This class doesn't keep references to its constant pool entries. + } + + + public void superClassConstantAccept(ConstantVisitor constantVisitor) + { + // This class doesn't keep references to its constant pool entries. + } + + + public void interfaceConstantsAccept(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))); + } + + + public void attributesAccept(AttributeVisitor attributeVisitor) + { + throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store attributes"); + } + + + public void attributeAccept(String name, 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; + } + + + // Implementations for Object. + + public String toString() + { + return "LibraryClass("+getName()+")"; + } +} diff --git a/src/proguard/classfile/LibraryField.java b/src/proguard/classfile/LibraryField.java new file mode 100644 index 000000000..1c1c427dd --- /dev/null +++ b/src/proguard/classfile/LibraryField.java @@ -0,0 +1,77 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 {@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz referencedClass; + + + /** + * Creates an uninitialized LibraryField. + */ + public LibraryField() + { + } + + + /** + * Creates an initialized LibraryField. + */ + public LibraryField(int u2accessFlags, + String name, + String descriptor) + { + super(u2accessFlags, name, descriptor); + } + + + // 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/LibraryMember.java b/src/proguard/classfile/LibraryMember.java new file mode 100644 index 000000000..8d8d00e08 --- /dev/null +++ b/src/proguard/classfile/LibraryMember.java @@ -0,0 +1,108 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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; + + + /** + * Creates an uninitialized LibraryMember. + */ + protected LibraryMember() + { + } + + + /** + * Creates an initialized LibraryMember. + */ + protected LibraryMember(int u2accessFlags, + String name, + String descriptor) + { + this.u2accessFlags = u2accessFlags; + this.name = name; + this.descriptor = descriptor; + } + + + /** + * 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/LibraryMethod.java b/src/proguard/classfile/LibraryMethod.java new file mode 100644 index 000000000..ac0785753 --- /dev/null +++ b/src/proguard/classfile/LibraryMethod.java @@ -0,0 +1,83 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 {@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized LibraryMethod. + */ + public LibraryMethod() + { + } + + + /** + * Creates an initialized LibraryMethod. + */ + public LibraryMethod(int u2accessFlags, + String name, + String descriptor) + { + super(u2accessFlags, name, descriptor); + } + + + // 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/Member.java b/src/proguard/classfile/Member.java new file mode 100644 index 000000000..ee2cf1949 --- /dev/null +++ b/src/proguard/classfile/Member.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.visitor.*; + +/** + * Representation of a field or method from a class. + * + * @author Eric Lafortune + */ +public interface Member extends VisitorAccepter +{ + /** + * Returns the access flags. + */ + public int getAccessFlags(); + + /** + * Returns the class member name. + */ + public String getName(Clazz clazz); + + /** + * Returns the class member's descriptor. + */ + public String getDescriptor(Clazz clazz); + + /** + * Accepts the given class visitor. + */ + public void accept(Clazz clazz, MemberVisitor memberVisitor); + + /** + * Lets the Clazz objects referenced in the descriptor string + * accept the given visitor. + */ + public void referencedClassesAccept(ClassVisitor classVisitor); +} diff --git a/src/proguard/classfile/Method.java b/src/proguard/classfile/Method.java new file mode 100644 index 000000000..64ff50a73 --- /dev/null +++ b/src/proguard/classfile/Method.java @@ -0,0 +1,32 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + + +/** + * Representation of a method from a class. + * + * @author Eric Lafortune + */ +public interface Method extends Member +{ +} diff --git a/src/proguard/classfile/ProgramClass.java b/src/proguard/classfile/ProgramClass.java new file mode 100644 index 000000000..54bb8b1de --- /dev/null +++ b/src/proguard/classfile/ProgramClass.java @@ -0,0 +1,572 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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) + { + throw ((IllegalStateException)new IllegalStateException("Expected Utf8Constant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex)); + } + } + + public String getStringString(int constantIndex) + { + try + { + return ((StringConstant)constantPool[constantIndex]).getString(this); + } + catch (ClassCastException ex) + { + throw ((IllegalStateException)new IllegalStateException("Expected StringConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex)); + } + } + + public String getClassName(int constantIndex) + { + try + { + return ((ClassConstant)constantPool[constantIndex]).getName(this); + } + catch (ClassCastException ex) + { + throw ((IllegalStateException)new IllegalStateException("Expected ClassConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex)); + } + } + + public String getName(int constantIndex) + { + try + { + return ((NameAndTypeConstant)constantPool[constantIndex]).getName(this); + } + catch (ClassCastException ex) + { + throw ((IllegalStateException)new IllegalStateException("Expected NameAndTypeConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex)); + } + } + + public String getType(int constantIndex) + { + try + { + return ((NameAndTypeConstant)constantPool[constantIndex]).getType(this); + } + catch (ClassCastException ex) + { + throw ((IllegalStateException)new IllegalStateException("Expected NameAndTypeConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex)); + } + } + + + public String getRefName(int constantIndex) + { + try + { + return ((RefConstant)constantPool[constantIndex]).getName(this); + } + catch (ClassCastException ex) + { + throw ((IllegalStateException)new IllegalStateException("Expected RefConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex)); + } + } + + public String getRefType(int constantIndex) + { + try + { + return ((RefConstant)constantPool[constantIndex]).getType(this); + } + catch (ClassCastException ex) + { + throw ((IllegalStateException)new IllegalStateException("Expected RefConstant at index ["+constantIndex+"] in class ["+getName()+"]").initCause(ex)); + } + } + + + 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 extends_(String className) + { + if (getName().equals(className)) + { + return true; + } + + Clazz superClass = getSuperClass(); + return superClass != null && + superClass.extends_(className); + } + + + 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 boolean extendsOrImplements(String className) + { + if (getName().equals(className)) + { + return true; + } + + Clazz superClass = getSuperClass(); + if (superClass != null && + superClass.extendsOrImplements(className)) + { + return true; + } + + for (int index = 0; index < u2interfacesCount; index++) + { + Clazz interfaceClass = getInterface(index); + if (interfaceClass != null && + interfaceClass.extendsOrImplements(className)) + { + 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) + { + // Visit the interfaces of the superclasses, if we haven't done so yet. + if (!visitSuperClass) + { + Clazz superClass = getSuperClass(); + if (superClass != null) + { + superClass.hierarchyAccept(false, + false, + true, + false, + classVisitor); + } + } + + // Visit the interfaces. + for (int index = 0; index < u2interfacesCount; index++) + { + Clazz interfaceClass = getInterface(index); + if (interfaceClass != null) + { + interfaceClass.hierarchyAccept(true, + false, + 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 subclassesAccept(ClassVisitor classVisitor) + { + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].accept(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 thisClassConstantAccept(ConstantVisitor constantVisitor) + { + constantPool[u2thisClass].accept(this, constantVisitor); + } + + + public void superClassConstantAccept(ConstantVisitor constantVisitor) + { + if (u2superClass != 0) + { + constantPool[u2superClass].accept(this, constantVisitor); + } + } + + + public void interfaceConstantsAccept(ConstantVisitor constantVisitor) + { + for (int index = 0; index < u2interfacesCount; index++) + { + constantPool[u2interfaces[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))); + } + + + public void attributesAccept(AttributeVisitor attributeVisitor) + { + for (int index = 0; index < u2attributesCount; index++) + { + attributes[index].accept(this, attributeVisitor); + } + } + + + public void attributeAccept(String name, AttributeVisitor attributeVisitor) + { + for (int index = 0; index < u2attributesCount; index++) + { + Attribute attribute = attributes[index]; + if (attribute.getAttributeName(this).equals(name)) + { + attribute.accept(this, attributeVisitor); + } + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } + + + // Implementations for Object. + + public String toString() + { + return "ProgramClass("+getName()+")"; + } +} diff --git a/src/proguard/classfile/ProgramField.java b/src/proguard/classfile/ProgramField.java new file mode 100644 index 000000000..3bdfd85ec --- /dev/null +++ b/src/proguard/classfile/ProgramField.java @@ -0,0 +1,93 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.attribute.Attribute; +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 {@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz referencedClass; + + + /** + * Creates an uninitialized ProgramField. + */ + public ProgramField() + { + } + + + /** + * Creates an initialized ProgramField. + */ + public ProgramField(int u2accessFlags, + int u2nameIndex, + int u2descriptorIndex, + int u2attributesCount, + Attribute[] attributes, + Clazz referencedClass) + { + super(u2accessFlags, u2nameIndex, u2descriptorIndex, u2attributesCount, attributes); + + this.referencedClass = referencedClass; + } + + + // 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/ProgramMember.java b/src/proguard/classfile/ProgramMember.java new file mode 100644 index 000000000..240d3440f --- /dev/null +++ b/src/proguard/classfile/ProgramMember.java @@ -0,0 +1,140 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + +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; + + + /** + * Creates an uninitialized ProgramMember. + */ + protected ProgramMember() + { + } + + + /** + * Creates an initialized ProgramMember. + */ + protected ProgramMember(int u2accessFlags, + int u2nameIndex, + int u2descriptorIndex, + int u2attributesCount, + Attribute[] attributes) + { + this.u2accessFlags = u2accessFlags; + this.u2nameIndex = u2nameIndex; + this.u2descriptorIndex = u2descriptorIndex; + this.u2attributesCount = u2attributesCount; + this.attributes = attributes; + } + + + /** + * 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/ProgramMethod.java b/src/proguard/classfile/ProgramMethod.java new file mode 100644 index 000000000..26f793fb4 --- /dev/null +++ b/src/proguard/classfile/ProgramMethod.java @@ -0,0 +1,99 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.attribute.Attribute; +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 {@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized ProgramMethod. + */ + public ProgramMethod() + { + } + + + /** + * Creates an initialized ProgramMethod. + */ + public ProgramMethod(int u2accessFlags, + int u2nameIndex, + int u2descriptorIndex, + int u2attributesCount, + Attribute[] attributes, + Clazz[] referencedClasses) + { + super(u2accessFlags, u2nameIndex, u2descriptorIndex, u2attributesCount, attributes); + + this.referencedClasses = referencedClasses; + } + + + // 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/VisitorAccepter.java b/src/proguard/classfile/VisitorAccepter.java new file mode 100644 index 000000000..9c7a062c9 --- /dev/null +++ b/src/proguard/classfile/VisitorAccepter.java @@ -0,0 +1,47 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + + + +/** + * 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. + * + * @author Eric Lafortune + */ +public interface VisitorAccepter +{ + /** + * Gets the visitor information of the visitor accepter. + */ + public Object getVisitorInfo(); + + + /** + * Sets the visitor information of the visitor accepter. + */ + public void setVisitorInfo(Object visitorInfo); +} diff --git a/src/proguard/classfile/attribute/Attribute.java b/src/proguard/classfile/attribute/Attribute.java new file mode 100644 index 000000000..f34a0eef6 --- /dev/null +++ b/src/proguard/classfile/attribute/Attribute.java @@ -0,0 +1,142 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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; + + + /** + * Create an uninitialized Attribute. + */ + protected Attribute() + { + } + + + /** + * Create an initialized Attribute. + */ + protected Attribute(int u2attributeNameIndex) + { + this.u2attributeNameIndex = u2attributeNameIndex; + } + + + /** + * 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/BootstrapMethodInfo.java b/src/proguard/classfile/attribute/BootstrapMethodInfo.java new file mode 100755 index 000000000..f24676613 --- /dev/null +++ b/src/proguard/classfile/attribute/BootstrapMethodInfo.java @@ -0,0 +1,89 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.visitor.ConstantVisitor; + +/** + * Representation of a bootstrap method. + * + * @author Eric Lafortune + */ +public class BootstrapMethodInfo implements VisitorAccepter +{ + public int u2methodHandleIndex; + public int u2methodArgumentCount; + public int[] u2methodArguments; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized BootstrapMethodInfo. + */ + public BootstrapMethodInfo() + { + } + + + /** + * Creates an initialized BootstrapMethodInfo. + */ + public BootstrapMethodInfo(int u2methodHandleIndex, + int u2methodArgumentCount, + int[] u2methodArguments) + { + this.u2methodHandleIndex = u2methodHandleIndex; + this.u2methodArgumentCount = u2methodArgumentCount; + this.u2methodArguments = u2methodArguments; + } + + + /** + * Applies the given constant pool visitor to the argument constants of the + * bootstrap method. + */ + public void methodArgumentsAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + for (int index = 0; index < u2methodArgumentCount; index++) + { + clazz.constantPoolEntryAccept(u2methodArguments[index], + constantVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/BootstrapMethodsAttribute.java b/src/proguard/classfile/attribute/BootstrapMethodsAttribute.java new file mode 100755 index 000000000..4471a75eb --- /dev/null +++ b/src/proguard/classfile/attribute/BootstrapMethodsAttribute.java @@ -0,0 +1,95 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Attribute represents a bootstrap methods attribute. + * + * @author Eric Lafortune + */ +public class BootstrapMethodsAttribute extends Attribute +{ + public int u2bootstrapMethodsCount; + public BootstrapMethodInfo[] bootstrapMethods; + + + /** + * Creates an uninitialized BootstrapMethodsAttribute. + */ + public BootstrapMethodsAttribute() + { + } + + + /** + * Creates an initialized BootstrapMethodsAttribute. + */ + public BootstrapMethodsAttribute(int u2attributeNameIndex, + int u2bootstrapMethodsCount, + BootstrapMethodInfo[] bootstrapMethods) + { + super(u2attributeNameIndex); + + this.u2bootstrapMethodsCount = u2bootstrapMethodsCount; + this.bootstrapMethods = bootstrapMethods; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitBootstrapMethodsAttribute(clazz, this); + } + + + /** + * Applies the given constant pool visitor to all bootstrap method info + * entries. + */ + public void bootstrapMethodEntriesAccept(Clazz clazz, BootstrapMethodInfoVisitor bootstrapMethodInfoVisitor) + { + for (int index = 0; index < u2bootstrapMethodsCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of BootstrapMethodInfo. + bootstrapMethodInfoVisitor.visitBootstrapMethodInfo(clazz, bootstrapMethods[index]); + } + } + + + /** + * Applies the given constant pool visitor to the specified bootstrap method + * info entry. + */ + public void bootstrapMethodEntryAccept(Clazz clazz, + int bootstrapMethodIndex, + BootstrapMethodInfoVisitor bootstrapMethodInfoVisitor) + { + // We don't need double dispatching here, since there is only one + // type of BootstrapMethodInfo. + bootstrapMethodInfoVisitor.visitBootstrapMethodInfo(clazz, bootstrapMethods[bootstrapMethodIndex]); + } +} diff --git a/src/proguard/classfile/attribute/CodeAttribute.java b/src/proguard/classfile/attribute/CodeAttribute.java new file mode 100644 index 000000000..b3d4f4c93 --- /dev/null +++ b/src/proguard/classfile/attribute/CodeAttribute.java @@ -0,0 +1,202 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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() + { + } + + + /** + * Creates an initialized CodeAttribute. + */ + public CodeAttribute(int u2attributeNameIndex, + int u2maxStack, + int u2maxLocals, + int u4codeLength, + byte[] code, + int u2exceptionTableLength, + ExceptionInfo[] exceptionTable, + int u2attributesCount, + Attribute[] attributes) + { + super(u2attributeNameIndex); + + this.u2maxStack = u2maxStack; + this.u2maxLocals = u2maxLocals; + this.u4codeLength = u4codeLength; + this.code = code; + this.u2exceptionTableLength = u2exceptionTableLength; + this.exceptionTable = exceptionTable; + this.u2attributesCount = u2attributesCount; + this.attributes = attributes; + } + + + /** + * 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/ConstantValueAttribute.java b/src/proguard/classfile/attribute/ConstantValueAttribute.java new file mode 100644 index 000000000..056e377b2 --- /dev/null +++ b/src/proguard/classfile/attribute/ConstantValueAttribute.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; + +/** + * This Attribute represents a constant value attribute. + * + * @author Eric Lafortune + */ +public class ConstantValueAttribute extends Attribute +{ + public int u2constantValueIndex; + + + /** + * Creates an uninitialized ConstantValueAttribute. + */ + public ConstantValueAttribute() + { + } + + + /** + * Creates an initialized ConstantValueAttribute. + */ + public ConstantValueAttribute(int u2attributeNameIndex, + int u2constantValueIndex) + { + super(u2attributeNameIndex); + + this.u2constantValueIndex = u2constantValueIndex; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitConstantValueAttribute(clazz, field, this); + } +} diff --git a/src/proguard/classfile/attribute/DeprecatedAttribute.java b/src/proguard/classfile/attribute/DeprecatedAttribute.java new file mode 100644 index 000000000..f668063bd --- /dev/null +++ b/src/proguard/classfile/attribute/DeprecatedAttribute.java @@ -0,0 +1,66 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; + +/** + * This Attribute represents a deprecated attribute. + * + * @author Eric Lafortune + */ +public class DeprecatedAttribute extends Attribute +{ + /** + * Creates an uninitialized DeprecatedAttribute. + */ + public DeprecatedAttribute() + { + } + + + /** + * Creates an initialized DeprecatedAttribute. + */ + public DeprecatedAttribute(int u2attributeNameIndex) + { + super(u2attributeNameIndex); + } + + + // 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/EnclosingMethodAttribute.java b/src/proguard/classfile/attribute/EnclosingMethodAttribute.java new file mode 100644 index 000000000..1c2ecc4bf --- /dev/null +++ b/src/proguard/classfile/attribute/EnclosingMethodAttribute.java @@ -0,0 +1,132 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + */ + public Clazz referencedClass; + + /** + * An extra field optionally pointing to the referenced Method object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + */ + public Method referencedMethod; + + + /** + * Creates an uninitialized EnclosingMethodAttribute. + */ + public EnclosingMethodAttribute() + { + } + + + /** + * Creates an initialized EnclosingMethodAttribute. + */ + public EnclosingMethodAttribute(int u2attributeNameIndex, + int u2classIndex, + int u2nameAndTypeIndex) + { + super(u2attributeNameIndex); + + this.u2classIndex = u2classIndex; + this.u2nameAndTypeIndex = u2nameAndTypeIndex; + } + + + /** + * 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 new file mode 100644 index 000000000..31512e5d3 --- /dev/null +++ b/src/proguard/classfile/attribute/ExceptionInfo.java @@ -0,0 +1,100 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.VisitorAccepter; + +/** + * Representation of an Exception table entry. + * + * @author Eric Lafortune + */ +public class ExceptionInfo implements VisitorAccepter +{ + public int u2startPC; + public int u2endPC; + public int u2handlerPC; + public int u2catchType; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized ExceptionInfo. + */ + public ExceptionInfo() + { + this(0, 0, 0, 0); + } + + + /** + * 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; + } + + + /** + * Returns whether the exception's try block contains the instruction at the + * given offset. + */ + public boolean isApplicable(int instructionOffset) + { + return instructionOffset >= u2startPC && + instructionOffset < u2endPC; + } + + + /** + * Returns whether the exception's try block overlaps with the specified + * block of instructions. + */ + public boolean isApplicable(int startOffset, int endOffset) + { + return u2startPC < endOffset && + u2endPC > startOffset; + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/ExceptionsAttribute.java b/src/proguard/classfile/attribute/ExceptionsAttribute.java new file mode 100644 index 000000000..ff7b84a13 --- /dev/null +++ b/src/proguard/classfile/attribute/ExceptionsAttribute.java @@ -0,0 +1,80 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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() + { + } + + + /** + * Creates an initialized ExceptionsAttribute. + */ + public ExceptionsAttribute(int u2attributeNameIndex, + int u2exceptionIndexTableLength, + int[] u2exceptionIndexTable) + { + super(u2attributeNameIndex); + + this.u2exceptionIndexTableLength = u2exceptionIndexTableLength; + this.u2exceptionIndexTable = u2exceptionIndexTable; + } + + + // 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(Clazz clazz, ConstantVisitor constantVisitor) + { + for (int index = 0; index < u2exceptionIndexTableLength; index++) + { + clazz.constantPoolEntryAccept(u2exceptionIndexTable[index], + constantVisitor); + } + } +} diff --git a/src/proguard/classfile/attribute/InnerClassesAttribute.java b/src/proguard/classfile/attribute/InnerClassesAttribute.java new file mode 100644 index 000000000..b08d104d5 --- /dev/null +++ b/src/proguard/classfile/attribute/InnerClassesAttribute.java @@ -0,0 +1,80 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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() + { + } + + + /** + * Creates an initialized InnerClassesAttribute. + */ + public InnerClassesAttribute(int u2attributeNameIndex, + int u2classesCount, + InnerClassesInfo[] classes) + { + super(u2attributeNameIndex); + + this.u2classesCount = u2classesCount; + this.classes = classes; + } + + // + + // 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 new file mode 100644 index 000000000..87b9de4ee --- /dev/null +++ b/src/proguard/classfile/attribute/InnerClassesInfo.java @@ -0,0 +1,119 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.visitor.ConstantVisitor; + +/** + * Representation of an Inner Classes table entry. + * + * @author Eric Lafortune + */ +public class InnerClassesInfo implements VisitorAccepter +{ + public int u2innerClassIndex; + public int u2outerClassIndex; + public int u2innerNameIndex; + public int u2innerClassAccessFlags; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Returns the inner class index. + */ + protected int getInnerClassIndex() + { + return u2innerClassIndex; + } + + /** + * Returns the name index. + */ + protected int getInnerNameIndex() + { + return u2innerNameIndex; + } + + /** + * Sets the name index. + */ + protected void setInnerNameIndex(int index) + { + u2innerNameIndex = index; + } + + + /** + * Applies the given constant pool visitor to the class constant of the + * inner class, if any. + */ + public void innerClassConstantAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2innerClassIndex != 0) + { + clazz.constantPoolEntryAccept(u2innerClassIndex, constantVisitor); + } + } + + + /** + * Applies the given constant pool visitor to the class constant of the + * outer class, if any. + */ + public void outerClassConstantAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2outerClassIndex != 0) + { + clazz.constantPoolEntryAccept(u2outerClassIndex, constantVisitor); + } + } + + + /** + * Applies the given constant pool visitor to the Utf8 constant of the + * inner name, if any. + */ + public void innerNameConstantAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2innerNameIndex != 0) + { + clazz.constantPoolEntryAccept(u2innerNameIndex, constantVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/LineNumberInfo.java b/src/proguard/classfile/attribute/LineNumberInfo.java new file mode 100644 index 000000000..1bcacefbc --- /dev/null +++ b/src/proguard/classfile/attribute/LineNumberInfo.java @@ -0,0 +1,50 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * Representation of an Line Number table entry. + * + * @author Eric Lafortune + */ +public class LineNumberInfo +{ + public int u2startPC; + public int u2lineNumber; + + + /** + * Creates an uninitialized LineNumberInfo. + */ + public LineNumberInfo() + { + } + + + /** + * Creates an initialized LineNumberInfo. + */ + public LineNumberInfo(int u2startPC, int u2lineNumber) + { + this.u2startPC = u2startPC; + this.u2lineNumber = u2lineNumber; + } +} diff --git a/src/proguard/classfile/attribute/LineNumberTableAttribute.java b/src/proguard/classfile/attribute/LineNumberTableAttribute.java new file mode 100644 index 000000000..c217119b3 --- /dev/null +++ b/src/proguard/classfile/attribute/LineNumberTableAttribute.java @@ -0,0 +1,150 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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() + { + } + + + /** + * Creates an initialized LineNumberTableAttribute. + */ + public LineNumberTableAttribute(int u2attributeNameIndex, + int u2lineNumberTableLength, + LineNumberInfo[] lineNumberTable) + { + super(u2attributeNameIndex); + + this.u2lineNumberTableLength = u2lineNumberTableLength; + this.lineNumberTable = lineNumberTable; + } + + + /** + * 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; + } + + + /** + * Returns the lowest line number, or 0 if there aren't any line numbers. + */ + public int getLowestLineNumber() + { + if (u2lineNumberTableLength == 0) + { + return 0; + } + + int lowestLineNumber = Integer.MAX_VALUE; + + for (int index = 0; index < u2lineNumberTableLength; index++) + { + int lineNumber = lineNumberTable[index].u2lineNumber; + if (lineNumber < lowestLineNumber) + { + lowestLineNumber = lineNumber; + } + } + + return lowestLineNumber; + } + + + /** + * Returns the highest line number, or 0 if there aren't any line numbers. + */ + public int getHighestLineNumber() + { + if (u2lineNumberTableLength == 0) + { + return 0; + } + + int highestLineNumber = Integer.MIN_VALUE; + + for (int index = 0; index < u2lineNumberTableLength; index++) + { + int lineNumber = lineNumberTable[index].u2lineNumber; + if (lineNumber > highestLineNumber) + { + highestLineNumber = lineNumber; + } + } + + return highestLineNumber; + } + + + // 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 new file mode 100644 index 000000000..00bbd505f --- /dev/null +++ b/src/proguard/classfile/attribute/LocalVariableInfo.java @@ -0,0 +1,117 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * Representation of an Local Variable table entry. + * + * @author Eric Lafortune + */ +public class LocalVariableInfo implements VisitorAccepter, Comparable +{ + public int u2startPC; + public int u2length; + public int u2nameIndex; + public int u2descriptorIndex; + public int u2index; + + /** + * An extra field pointing to the referenced Clazz object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + */ + public Clazz referencedClass; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized LocalVariableInfo. + */ + public LocalVariableInfo() + { + } + + + /** + * Creates an initialized LocalVariableInfo. + */ + public LocalVariableInfo(int u2startPC, + int u2length, + int u2nameIndex, + int u2descriptorIndex, + int u2index) + { + this.u2startPC = u2startPC; + this.u2length = u2length; + this.u2nameIndex = u2nameIndex; + this.u2descriptorIndex = u2descriptorIndex; + this.u2index = u2index; + } + + + /** + * Lets the referenced class accept the given visitor. + */ + public void referencedClassAccept(ClassVisitor classVisitor) + { + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } + + + // Implementations for Comparable. + + public int compareTo(Object object) + { + LocalVariableInfo other = (LocalVariableInfo)object; + + return + this.u2startPC < other.u2startPC ? -1 : this.u2startPC > other.u2startPC ? 1 : + this.u2index < other.u2index ? -1 : this.u2index > other.u2index ? 1 : + this.u2length < other.u2length ? -1 : this.u2length > other.u2length ? 1 : + this.u2descriptorIndex < other.u2descriptorIndex ? -1 : this.u2descriptorIndex > other.u2descriptorIndex ? 1 : + this.u2nameIndex < other.u2nameIndex ? -1 : this.u2nameIndex > other.u2nameIndex ? 1 : + 0; + } +} diff --git a/src/proguard/classfile/attribute/LocalVariableTableAttribute.java b/src/proguard/classfile/attribute/LocalVariableTableAttribute.java new file mode 100644 index 000000000..9e081c015 --- /dev/null +++ b/src/proguard/classfile/attribute/LocalVariableTableAttribute.java @@ -0,0 +1,79 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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() + { + } + + + /** + * Creates an initialized LocalVariableTableAttribute. + */ + public LocalVariableTableAttribute(int u2attributeNameIndex, + int u2localVariableTableLength, + LocalVariableInfo[] localVariableTable) + { + super(u2attributeNameIndex); + + this.u2localVariableTableLength = u2localVariableTableLength; + this.localVariableTable = localVariableTable; + } + + + // 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 new file mode 100644 index 000000000..15b9d246a --- /dev/null +++ b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java @@ -0,0 +1,125 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * Representation of an Local Variable table entry. + * + * @author Eric Lafortune + */ +public class LocalVariableTypeInfo implements VisitorAccepter, Comparable +{ + public int u2startPC; + public int u2length; + public int u2nameIndex; + public int u2signatureIndex; + public int u2index; + + /** + * An extra field pointing to the Clazz objects referenced in the + * type string. This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized LocalVariableTypeInfo. + */ + public LocalVariableTypeInfo() + { + } + + + /** + * Creates an initialized LocalVariableTypeInfo. + */ + public LocalVariableTypeInfo(int u2startPC, + int u2length, + int u2nameIndex, + int u2signatureIndex, + int u2index) + { + this.u2startPC = u2startPC; + this.u2length = u2length; + this.u2nameIndex = u2nameIndex; + this.u2signatureIndex = u2signatureIndex; + this.u2index = u2index; + } + + + /** + * Applies the given visitor to all referenced classes. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + Clazz referencedClass = referencedClasses[index]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } + + + // Implementations for Comparable. + + public int compareTo(Object object) + { + LocalVariableTypeInfo other = (LocalVariableTypeInfo)object; + + return + this.u2startPC < other.u2startPC ? -1 : this.u2startPC > other.u2startPC ? 1 : + this.u2length < other.u2length ? -1 : this.u2length > other.u2length ? 1 : + this.u2index < other.u2index ? -1 : this.u2index > other.u2index ? 1 : + this.u2signatureIndex < other.u2signatureIndex ? -1 : this.u2signatureIndex > other.u2signatureIndex ? 1 : + this.u2nameIndex < other.u2nameIndex ? -1 : this.u2nameIndex > other.u2nameIndex ? 1 : + 0; + } +} diff --git a/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java b/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java new file mode 100644 index 000000000..dda24fb88 --- /dev/null +++ b/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java @@ -0,0 +1,79 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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() + { + } + + + /** + * Creates an initialized LocalVariableTypeTableAttribute. + */ + public LocalVariableTypeTableAttribute(int u2attributeNameIndex, + int u2localVariableTypeTableLength, + LocalVariableTypeInfo[] localVariableTypeTable) + { + super(u2attributeNameIndex); + + this.u2localVariableTypeTableLength = u2localVariableTypeTableLength; + this.localVariableTypeTable = localVariableTypeTable; + } + + + // 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/SignatureAttribute.java b/src/proguard/classfile/attribute/SignatureAttribute.java new file mode 100644 index 000000000..86f95b826 --- /dev/null +++ b/src/proguard/classfile/attribute/SignatureAttribute.java @@ -0,0 +1,100 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 {@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized SignatureAttribute. + */ + public SignatureAttribute() + { + } + + + /** + * Creates an initialized SignatureAttribute. + */ + public SignatureAttribute(int u2attributeNameIndex, + int u2signatureIndex) + { + super(u2attributeNameIndex); + + this.u2signatureIndex = u2signatureIndex; + } + + + /** + * 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/SourceDirAttribute.java b/src/proguard/classfile/attribute/SourceDirAttribute.java new file mode 100644 index 000000000..faa1c79b2 --- /dev/null +++ b/src/proguard/classfile/attribute/SourceDirAttribute.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.Clazz; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a source directory attribute. + * + * @author Eric Lafortune + */ +public class SourceDirAttribute extends Attribute +{ + public int u2sourceDirIndex; + + + /** + * Creates an uninitialized SourceDirAttribute. + */ + public SourceDirAttribute() + { + } + + + /** + * Creates an initialized SourceDirAttribute. + */ + public SourceDirAttribute(int u2attributeNameIndex, + int u2sourceDirIndex) + { + super(u2attributeNameIndex); + + this.u2sourceDirIndex = u2sourceDirIndex; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSourceDirAttribute(clazz, this); + } +} diff --git a/src/proguard/classfile/attribute/SourceFileAttribute.java b/src/proguard/classfile/attribute/SourceFileAttribute.java new file mode 100644 index 000000000..4abac170f --- /dev/null +++ b/src/proguard/classfile/attribute/SourceFileAttribute.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.Clazz; +import proguard.classfile.attribute.visitor.AttributeVisitor; + +/** + * This Attribute represents a source file attribute. + * + * @author Eric Lafortune + */ +public class SourceFileAttribute extends Attribute +{ + public int u2sourceFileIndex; + + + /** + * Creates an uninitialized SourceFileAttribute. + */ + public SourceFileAttribute() + { + } + + + /** + * Creates an initialized SourceFileAttribute. + */ + public SourceFileAttribute(int u2attributeNameIndex, + int u2sourceFileIndex) + { + super(u2attributeNameIndex); + + this.u2sourceFileIndex = u2sourceFileIndex; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitSourceFileAttribute(clazz, this); + } +} diff --git a/src/proguard/classfile/attribute/SyntheticAttribute.java b/src/proguard/classfile/attribute/SyntheticAttribute.java new file mode 100644 index 000000000..013533036 --- /dev/null +++ b/src/proguard/classfile/attribute/SyntheticAttribute.java @@ -0,0 +1,66 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; + +/** + * This Attribute represents a synthetic attribute. + * + * @author Eric Lafortune + */ +public class SyntheticAttribute extends Attribute +{ + /** + * Creates an uninitialized SyntheticAttribute. + */ + public SyntheticAttribute() + { + } + + + /** + * Creates an initialized SyntheticAttribute. + */ + public SyntheticAttribute(int u2attributeNameIndex) + { + super(u2attributeNameIndex); + } + + + // 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/UnknownAttribute.java b/src/proguard/classfile/attribute/UnknownAttribute.java new file mode 100644 index 000000000..182a9e85c --- /dev/null +++ b/src/proguard/classfile/attribute/UnknownAttribute.java @@ -0,0 +1,82 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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; + } + + + /** + * Creates an initialized UnknownAttribute. + */ + public UnknownAttribute(int u2attributeNameIndex, + int u4attributeLength, + byte[] info) + { + super(u2attributeNameIndex); + + this.u4attributeLength = u4attributeLength; + this.info = info; + } + + + // 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 new file mode 100644 index 000000000..b2f18d5ac --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/Annotation.java @@ -0,0 +1,143 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.visitor.ElementValueVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * Representation of an annotation. + * + * @author Eric Lafortune + */ +public class Annotation implements VisitorAccepter +{ + public int u2typeIndex; + public int u2elementValuesCount; + public ElementValue[] elementValues; + + /** + * An extra field pointing to the Clazz objects referenced in the + * type string. This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized Annotation. + */ + public Annotation() + { + } + + + /** + * Creates an initialized Annotation. + */ + public Annotation(int u2typeIndex, + int u2elementValuesCount, + ElementValue[] elementValues) + { + this.u2typeIndex = u2typeIndex; + this.u2elementValuesCount = u2elementValuesCount; + this.elementValues = elementValues; + } + + + /** + * Returns the type. + */ + public String getType(Clazz clazz) + { + return clazz.getString(u2typeIndex); + } + + + + /** + * Applies the given visitor to the first referenced class. This is the + * main annotation class. + */ + public void referencedClassAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + Clazz referencedClass = referencedClasses[0]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + + + /** + * Applies the given visitor to all referenced classes. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + Clazz referencedClass = referencedClasses[index]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + } + + + /** + * Applies the given visitor to all element value pairs. + */ + public void elementValuesAccept(Clazz clazz, ElementValueVisitor elementValueVisitor) + { + for (int index = 0; index < u2elementValuesCount; index++) + { + elementValues[index].accept(clazz, this, elementValueVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java new file mode 100644 index 000000000..b8f29cc1b --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java @@ -0,0 +1,73 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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() + { + } + + + /** + * Creates an initialized AnnotationDefaultAttribute. + */ + public AnnotationDefaultAttribute(int u2attributeNameIndex, + ElementValue defaultValue) + { + super(u2attributeNameIndex); + + this.defaultValue = defaultValue; + } + + + /** + * 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 new file mode 100644 index 000000000..8354f5dc9 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java @@ -0,0 +1,76 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.visitor.*; + +/** + * This ElementValue represents an annotation element value. + * + * @author Eric Lafortune + */ +public class AnnotationElementValue extends ElementValue +{ + public Annotation annotationValue; + + + /** + * Creates an uninitialized AnnotationElementValue. + */ + public AnnotationElementValue() + { + } + + + /** + * Creates an initialized AnnotationElementValue. + */ + public AnnotationElementValue(int u2elementNameIndex, + Annotation annotationValue) + { + super(u2elementNameIndex); + + this.annotationValue = annotationValue; + } + + + /** + * Applies the given visitor to the annotation. + */ + public void annotationAccept(Clazz clazz, AnnotationVisitor annotationVisitor) + { + annotationVisitor.visitAnnotation(clazz, annotationValue); + } + + + // Implementations for ElementValue. + + public char getTag() + { + return ClassConstants.ELEMENT_VALUE_ANNOTATION; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + 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 000000000..a936ff462 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java @@ -0,0 +1,100 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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; + + + /** + * Creates an uninitialized AnnotationsAttribute. + */ + protected AnnotationsAttribute() + { + } + + + /** + * Creates an initialized AnnotationsAttribute. + */ + protected AnnotationsAttribute(int u2attributeNameIndex, + int u2annotationsCount, + Annotation[] annotations) + { + super(u2attributeNameIndex); + + this.u2annotationsCount = u2annotationsCount; + this.annotations = annotations; + } + + + /** + * 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 new file mode 100644 index 000000000..0aab49bf9 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java @@ -0,0 +1,82 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.visitor.ElementValueVisitor; + +/** + * This ElementValue represents an array element value. + * + * @author Eric Lafortune + */ +public class ArrayElementValue extends ElementValue +{ + public int u2elementValuesCount; + public ElementValue[] elementValues; + + + /** + * Creates an uninitialized ArrayElementValue. + */ + public ArrayElementValue() + { + } + + + /** + * Creates an initialized ArrayElementValue. + */ + public ArrayElementValue(int u2elementNameIndex, + int u2elementValuesCount, + ElementValue[] elementValues) + { + super(u2elementNameIndex); + + this.u2elementValuesCount = u2elementValuesCount; + this.elementValues = elementValues; + } + + + // Implementations for ElementValue. + + public char getTag() + { + return ClassConstants.ELEMENT_VALUE_ARRAY; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + elementValueVisitor.visitArrayElementValue(clazz, annotation, this); + } + + + /** + * Applies the given visitor to all nested element values. + */ + public void elementValuesAccept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + for (int index = 0; index < u2elementValuesCount; index++) + { + elementValues[index].accept(clazz, annotation, elementValueVisitor); + } + } +} diff --git a/src/proguard/classfile/attribute/annotation/ClassElementValue.java b/src/proguard/classfile/attribute/annotation/ClassElementValue.java new file mode 100644 index 000000000..ffeaf7141 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ClassElementValue.java @@ -0,0 +1,95 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.visitor.ElementValueVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ElementValue represents a class element value. + * + * @author Eric Lafortune + */ +public class ClassElementValue extends ElementValue +{ + public int u2classInfoIndex; + + /** + * An extra field pointing to the Clazz objects referenced in the + * type name string. This field is filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized ClassElementValue. + */ + public ClassElementValue() + { + } + + + /** + * Creates an initialized ClassElementValue. + */ + public ClassElementValue(int u2elementNameIndex, + int u2classInfoIndex) + { + super(u2elementNameIndex); + + this.u2classInfoIndex = u2classInfoIndex; + } + + + /** + * Applies the given visitor to all referenced classes. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + Clazz referencedClass = referencedClasses[index]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + } + + + // Implementations for ElementValue. + + public char getTag() + { + return ClassConstants.ELEMENT_VALUE_CLASS; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + elementValueVisitor.visitClassElementValue(clazz, annotation, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java new file mode 100644 index 000000000..8be432934 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.Clazz; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; + +/** + * This ElementValue represents a constant element value. + * + * @author Eric Lafortune + */ +public class ConstantElementValue extends ElementValue +{ + public final char u1tag; + public int u2constantValueIndex; + + + /** + * Creates an uninitialized ConstantElementValue. + */ + public ConstantElementValue(char u1tag) + { + this.u1tag = u1tag; + } + + + /** + * Creates an initialized ConstantElementValue. + */ + public ConstantElementValue(char u1tag, + int u2elementNameIndex, + int u2constantValueIndex) + { + super(u2elementNameIndex); + + this.u1tag = u1tag; + this.u2constantValueIndex = u2constantValueIndex; + } + + + // Implementations for ElementValue. + + public char getTag() + { + return u1tag; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + elementValueVisitor.visitConstantElementValue(clazz, annotation, this); + } +} diff --git a/src/proguard/classfile/attribute/annotation/ElementValue.java b/src/proguard/classfile/attribute/annotation/ElementValue.java new file mode 100644 index 000000000..19a71980a --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ElementValue.java @@ -0,0 +1,126 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.visitor.ElementValueVisitor; +import proguard.classfile.visitor.MemberVisitor; + +/** + * 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 +{ + /** + * An extra field for the optional element name. It is used in element value + * pairs of annotations. Otherwise, it is 0. + */ + public int u2elementNameIndex; + + /** + * An extra field pointing to the referenced Clazz + * object, if applicable. This field is typically filled out by the + * {@link proguard.classfile.util.ClassReferenceInitializer}. + */ + public Clazz referencedClass; + + /** + * An extra field pointing to the referenced Method + * object, if applicable. This field is typically filled out by the + * {@link proguard.classfile.util.ClassReferenceInitializer}. + */ + public Method referencedMethod; + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized ElementValue. + */ + protected ElementValue() + { + } + + + /** + * Creates an initialized ElementValue. + */ + protected ElementValue(int u2elementNameIndex) + { + this.u2elementNameIndex = u2elementNameIndex; + } + + + /** + * Returns the element name. + */ + public String getMethodName(Clazz clazz) + { + return clazz.getString(u2elementNameIndex); + } + + + // Abstract methods to be implemented by extensions. + + /** + * Returns the tag of this element value. + */ + public abstract char getTag(); + + + /** + * Accepts the given visitor. + */ + public abstract void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor); + + + + /** + * Applies the given visitor to the referenced method. + */ + public void referencedMethodAccept(MemberVisitor memberVisitor) + { + if (referencedMethod != null) + { + referencedMethod.accept(referencedClass, memberVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java new file mode 100644 index 000000000..cd0f2f984 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java @@ -0,0 +1,138 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.visitor.ElementValueVisitor; +import proguard.classfile.visitor.*; + +/** + * This ElementValue represents an enumeration constant element value. + * + * @author Eric Lafortune + */ +public class EnumConstantElementValue extends ElementValue +{ + public int u2typeNameIndex; + public int u2constantNameIndex; + + /** + * An extra field pointing to the Clazz objects referenced in the + * type name string. This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + /** + * An extra field optionally pointing to the referenced enum Field object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + */ + public Field referencedField; + + + /** + * Creates an uninitialized EnumConstantElementValue. + */ + public EnumConstantElementValue() + { + } + + + /** + * Creates an initialized EnumConstantElementValue. + */ + public EnumConstantElementValue(int u2elementNameIndex, + int u2typeNameIndex, + int u2constantNameIndex) + { + super(u2elementNameIndex); + + this.u2typeNameIndex = u2typeNameIndex; + this.u2constantNameIndex = u2constantNameIndex; + } + + + /** + * Returns the enumeration type name. + */ + public String getTypeName(Clazz clazz) + { + return clazz.getString(u2typeNameIndex); + } + + + /** + * Returns the constant name. + */ + public String getConstantName(Clazz clazz) + { + return clazz.getString(u2constantNameIndex); + } + + + /** + * Applies the given visitor to all referenced classes. + */ + public void referencedClassesAccept(ClassVisitor classVisitor) + { + if (referencedClasses != null) + { + for (int index = 0; index < referencedClasses.length; index++) + { + Clazz referencedClass = referencedClasses[index]; + if (referencedClass != null) + { + referencedClass.accept(classVisitor); + } + } + } + } + + + /** + * Applies the given visitor to the referenced field. + */ + public void referencedFieldAccept(MemberVisitor memberVisitor) + { + if (referencedField != null) + { + referencedField.accept(referencedClasses[0], + memberVisitor); + } + } + + + // Implementations for ElementValue. + + public char getTag() + { + return ClassConstants.ELEMENT_VALUE_ENUM_CONSTANT; + } + + public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor) + { + 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 000000000..ddaa3a6d1 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java @@ -0,0 +1,83 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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; + + + /** + * Creates an uninitialized ParameterAnnotationsAttribute. + */ + protected ParameterAnnotationsAttribute() + { + } + + + /** + * Creates an initialized ParameterAnnotationsAttribute. + */ + protected ParameterAnnotationsAttribute(int u2attributeNameIndex, + int u2parametersCount, + int[] u2parameterAnnotationsCount, + Annotation[][] parameterAnnotations) + { + super(u2attributeNameIndex); + + this.u2parametersCount = u2parametersCount; + this.u2parameterAnnotationsCount = u2parameterAnnotationsCount; + this.parameterAnnotations = parameterAnnotations; + } + + + /** + * 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/RuntimeInvisibleAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java new file mode 100644 index 000000000..deda8a53d --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java @@ -0,0 +1,70 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; + +/** + * This Attribute represents a runtime invisible annotations attribute. + * + * @author Eric Lafortune + */ +public class RuntimeInvisibleAnnotationsAttribute extends AnnotationsAttribute +{ + /** + * Creates an uninitialized RuntimeInvisibleAnnotationsAttribute. + */ + public RuntimeInvisibleAnnotationsAttribute() + { + } + + + /** + * Creates an initialized RuntimeInvisibleAnnotationsAttribute. + */ + public RuntimeInvisibleAnnotationsAttribute(int u2attributeNameIndex, + int u2annotationsCount, + Annotation[] annotations) + { + super(u2attributeNameIndex, u2annotationsCount, annotations); + } + + +// 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/RuntimeInvisibleParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java new file mode 100644 index 000000000..2fcae8848 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; + +/** + * This Attribute represents a runtime invisible parameter annotations attribute. + * + * @author Eric Lafortune + */ +public class RuntimeInvisibleParameterAnnotationsAttribute extends ParameterAnnotationsAttribute +{ + /** + * Creates an uninitialized RuntimeInvisibleParameterAnnotationsAttribute. + */ + public RuntimeInvisibleParameterAnnotationsAttribute() + { + } + + + /** + * Creates an initialized RuntimeInvisibleParameterAnnotationsAttribute. + */ + public RuntimeInvisibleParameterAnnotationsAttribute(int u2attributeNameIndex, + int u2parametersCount, + int[] u2parameterAnnotationsCount, + Annotation[][] parameterAnnotations) + { + super(u2attributeNameIndex, + u2parametersCount, + u2parameterAnnotationsCount, + parameterAnnotations); + } + + + // 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/RuntimeVisibleAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java new file mode 100644 index 000000000..da94f0c87 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java @@ -0,0 +1,70 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; + +/** + * This Attribute represents a runtime visible annotations attribute. + * + * @author Eric Lafortune + */ +public class RuntimeVisibleAnnotationsAttribute extends AnnotationsAttribute +{ + /** + * Creates an uninitialized RuntimeVisibleAnnotationsAttribute. + */ + public RuntimeVisibleAnnotationsAttribute() + { + } + + + /** + * Creates an initialized RuntimeVisibleAnnotationsAttribute. + */ + public RuntimeVisibleAnnotationsAttribute(int u2attributeNameIndex, + int u2annotationsCount, + Annotation[] annotations) + { + super(u2attributeNameIndex, u2annotationsCount, annotations); + } + + + // 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/RuntimeVisibleParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java new file mode 100644 index 000000000..caa683057 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; + +/** + * This Attribute represents a runtime visible parameter annotations attribute. + * + * @author Eric Lafortune + */ +public class RuntimeVisibleParameterAnnotationsAttribute extends ParameterAnnotationsAttribute +{ + /** + * Creates an uninitialized RuntimeVisibleParameterAnnotationsAttribute. + */ + public RuntimeVisibleParameterAnnotationsAttribute() + { + } + + + /** + * Creates an initialized RuntimeVisibleParameterAnnotationsAttribute. + */ + public RuntimeVisibleParameterAnnotationsAttribute(int u2attributeNameIndex, + int u2parametersCount, + int[] u2parameterAnnotationsCount, + Annotation[][] parameterAnnotations) + { + super(u2attributeNameIndex, + u2parametersCount, + u2parameterAnnotationsCount, + parameterAnnotations); + } + + + // 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/package.html b/src/proguard/classfile/attribute/annotation/package.html new file mode 100644 index 000000000..6aacff3e2 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/package.html @@ -0,0 +1,4 @@ + +This package contains classes to represent the annotation attributes inside +class files. + 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 000000000..0aadfe3ff --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java @@ -0,0 +1,105 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + + /** + * Creates a new AllAnnotationVisitor. + * @param annotationVisitor the AnnotationVisitor to which visits will be + * delegated. + */ + 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/AllElementValueVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java new file mode 100644 index 000000000..b728b8b5b --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java @@ -0,0 +1,201 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; +import proguard.util.*; + +/** + * This AttributeVisitor and AnnotationVisitor lets a given ElementValueVisitor + * visit all ElementValue objects of the attributes or annotations that it + * visits. + * + * @author Eric Lafortune + */ +public class AllElementValueVisitor +extends SimplifiedVisitor +implements AttributeVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private final boolean deep; + private final ElementValueVisitor elementValueVisitor; + + + /** + * Creates a new AllElementValueVisitor. + * @param elementValueVisitor the AllElementValueVisitor to which visits + * will be delegated. + */ + public AllElementValueVisitor(ElementValueVisitor elementValueVisitor) + { + this(false, elementValueVisitor); + } + + + /** + * Creates a new AllElementValueVisitor. + * @param deep specifies whether the element values + * further down the hierarchy should be + * visited too. + * @param elementValueVisitor the AllElementValueVisitor to which visits + * will be delegated. + */ + public AllElementValueVisitor(boolean deep, + ElementValueVisitor elementValueVisitor) + { + this.deep = deep; + this.elementValueVisitor = elementValueVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, field, this); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, field, this); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + // Visit the annotations. + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Visit the 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 AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + annotation.elementValuesAccept(clazz, this); + } + + + public void visitAnnotation(Clazz clazz, Field field, Annotation annotation) + { + annotation.elementValuesAccept(clazz, this); + } + + + public void visitAnnotation(Clazz clazz, Method method, Annotation annotation) + { + annotation.elementValuesAccept(clazz, this); + } + + + public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation) + { + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + elementValueVisitor.visitConstantElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + elementValueVisitor.visitEnumConstantElementValue(clazz, annotation, enumConstantElementValue); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + elementValueVisitor.visitClassElementValue(clazz, annotation, classElementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + elementValueVisitor.visitAnnotationElementValue(clazz, annotation, annotationElementValue); + + if (deep) + { + annotationElementValue.annotationAccept(clazz, this); + } + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + elementValueVisitor.visitArrayElementValue(clazz, annotation, arrayElementValue); + + if (deep) + { + arrayElementValue.elementValuesAccept(clazz, annotation, elementValueVisitor); + } + } +} 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 000000000..305928eb7 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..7833f7e42 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..d6ec3cad7 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java @@ -0,0 +1,101 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 AnnotationVisitor delegates its visits to another given + * AnnotationVisitor, but only when the visited annotation has + * a type that matches a given regular expression. + * + * @author Eric Lafortune + */ +public class AnnotationTypeFilter +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 annotationVisitor 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/visitor/AnnotationVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java new file mode 100644 index 000000000..8d207afb5 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java @@ -0,0 +1,40 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This interface specifies the methods for a visitor of + * Annotation objects. Note that there is only a single + * implementation of Annotation, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface AnnotationVisitor +{ + 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 000000000..2d3a20dc1 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ElementValue + * 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 000000000..10d064850 --- /dev/null +++ b/src/proguard/classfile/attribute/annotation/visitor/package.html @@ -0,0 +1,3 @@ + +This package contains visitors for annotation attributes and their components. + diff --git a/src/proguard/classfile/attribute/package.html b/src/proguard/classfile/attribute/package.html new file mode 100644 index 000000000..d17caaafd --- /dev/null +++ b/src/proguard/classfile/attribute/package.html @@ -0,0 +1,3 @@ + +This package contains classes to represent the attributes inside class files. + diff --git a/src/proguard/classfile/attribute/preverification/DoubleType.java b/src/proguard/classfile/attribute/preverification/DoubleType.java new file mode 100644 index 000000000..1bc3e5d0a --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..c58cd0a81 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..4f0d72e47 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..9c43cae1c --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..f722d73c5 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..c98616571 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..be74df0f7 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..fe0d85fb7 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..4ea370a2e --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..4384aaebc --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..a3bd824d0 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..51e69fb9a --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..01890f307 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..3de059f42 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..02dd1a207 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..7b2bfa931 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..7e4a0fdb0 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..02c38722f --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..ada9ce881 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..5e4cf8874 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 + * StackMapFrame 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 000000000..2a3e9e70d --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 + * VerificationType 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 000000000..9d8801c3e --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/AllBootstrapMethodInfoVisitor.java b/src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java new file mode 100644 index 000000000..d70803c33 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This AttributeVisitor lets a given BootstrapMethodInfoVisitor visit all + * bootstrap method objects of the BootstrapMethodsAttribute objects it visits. + * + * @author Eric Lafortune + */ +public class AllBootstrapMethodInfoVisitor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final BootstrapMethodInfoVisitor bootstrapMethodInfoVisitor; + + + public AllBootstrapMethodInfoVisitor(BootstrapMethodInfoVisitor bootstrapMethodInfoVisitor) + { + this.bootstrapMethodInfoVisitor = bootstrapMethodInfoVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + bootstrapMethodsAttribute.bootstrapMethodEntriesAccept(clazz, bootstrapMethodInfoVisitor); + } +} diff --git a/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java b/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java new file mode 100644 index 000000000..927bfd962 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This AttributeVisitor lets a given ExceptionInfoVisitor visit all exceptions + * objects of the CodeAttribute objects it visits. + * + * @author Eric Lafortune + */ +public class AllExceptionInfoVisitor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final ExceptionInfoVisitor exceptionInfoVisitor; + + + public AllExceptionInfoVisitor(ExceptionInfoVisitor exceptionInfoVisitor) + { + this.exceptionInfoVisitor = exceptionInfoVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.exceptionsAccept(clazz, method, exceptionInfoVisitor); + } +} diff --git a/src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java b/src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java new file mode 100644 index 000000000..242221820 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This AttributeVisitor lets a given InnerClassesInfoVisitor visit all + * InnerClassessInfo objects of the InnerClassesAttribute objects it visits. + * + * @author Eric Lafortune + */ +public class AllInnerClassesInfoVisitor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final InnerClassesInfoVisitor innerClassesInfoVisitor; + + + public AllInnerClassesInfoVisitor(InnerClassesInfoVisitor innerClassesInfoVisitor) + { + this.innerClassesInfoVisitor = innerClassesInfoVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + innerClassesAttribute.innerClassEntriesAccept(clazz, innerClassesInfoVisitor); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java b/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java new file mode 100644 index 000000000..0db77d559 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java @@ -0,0 +1,386 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +import java.util.List; + +/** + * This AttributeVisitor delegates its visits another AttributeVisitor, but + * only when the visited attribute has a name that that matches a given regular + * expression. + * + * @author Eric Lafortune + */ +public class AttributeNameFilter +implements AttributeVisitor +{ + private final StringMatcher regularExpressionMatcher; + private final AttributeVisitor attributeVisitor; + + + /** + * Creates a new AttributeNameFilter. + * @param regularExpression the regular expression against which attribute + * names will be matched. + * @param attributeVisitor the AttributeVisitor to which + * visits will be delegated. + */ + public AttributeNameFilter(String regularExpression, + AttributeVisitor attributeVisitor) + { + this(new ListParser(new NameParser()).parse(regularExpression), + attributeVisitor); + } + + + /** + * Creates a new AttributeNameFilter. + * @param regularExpression the regular expression against which attribute + * names will be matched. + * @param attributeVisitor the AttributeVisitor to which + * visits will be delegated. + */ + public AttributeNameFilter(List regularExpression, + AttributeVisitor attributeVisitor) + { + this(new ListParser(new NameParser()).parse(regularExpression), + attributeVisitor); + } + + + /** + * Creates a new AttributeNameFilter. + * @param regularExpressionMatcher the string matcher against which + * attribute names will be matched. + * @param attributeVisitor the AttributeVisitor to + * which visits will be delegated. + */ + public AttributeNameFilter(StringMatcher regularExpressionMatcher, + AttributeVisitor attributeVisitor) + { + this.regularExpressionMatcher = regularExpressionMatcher; + this.attributeVisitor = attributeVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + if (accepted(clazz, unknownAttribute)) + { + unknownAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + if (accepted(clazz, bootstrapMethodsAttribute)) + { + bootstrapMethodsAttribute.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 regularExpressionMatcher.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 000000000..76c1ab931 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/AttributeVisitor.java @@ -0,0 +1,90 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 Attribute + * objects. + * + * @author Eric Lafortune + */ +public interface AttributeVisitor +{ + // Attributes that are attached to classes. + + public void visitUnknownAttribute( Clazz clazz, UnknownAttribute unknownAttribute); + public void visitBootstrapMethodsAttribute( Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute); + 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/visitor/BootstrapMethodInfoVisitor.java b/src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java new file mode 100755 index 000000000..9aab92ef7 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java @@ -0,0 +1,40 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.beans.MethodDescriptor; + + +/** + * This interface specifies the methods for a visitor of + * BootstrapMethodInfo objects. Note that there is only a single + * implementation of BootstrapMethodInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface BootstrapMethodInfoVisitor +{ + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java b/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java new file mode 100644 index 000000000..4a765f2aa --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java @@ -0,0 +1,37 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +/** + * This interface specifies the methods for a visitor of + * ExceptionInfo objects. Note that there is only a single + * implementation of ExceptionInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface ExceptionInfoVisitor +{ + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java b/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java new file mode 100644 index 000000000..b373493cb --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java @@ -0,0 +1,38 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.Clazz; +import proguard.classfile.attribute.InnerClassesInfo; + + +/** + * This interface specifies the methods for a visitor of + * InnerClassesInfo objects. Note that there is only a single + * implementation of InnerClassesInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface InnerClassesInfoVisitor +{ + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java new file mode 100644 index 000000000..189b3d0f7 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java @@ -0,0 +1,38 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + + +/** + * This interface specifies the methods for a visitor of + * LineNumberInfo objects. Note that there is only a single + * implementation of LineNumberInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface LineNumberInfoVisitor +{ + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java new file mode 100644 index 000000000..c888d6330 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java @@ -0,0 +1,38 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + + +/** + * This interface specifies the methods for a visitor of + * LocalVariableInfo objects. Note that there is only a single + * implementation of LocalVariableInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface LocalVariableInfoVisitor +{ + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo); +} diff --git a/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java new file mode 100644 index 000000000..f992e6dad --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java @@ -0,0 +1,38 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + + +/** + * This interface specifies the methods for a visitor of + * LocalVariableTypeInfo objects. Note that there is only a single + * implementation of LocalVariableTypeInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface LocalVariableTypeInfoVisitor +{ + 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 000000000..37c0639f1 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java @@ -0,0 +1,365 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitBootstrapMethodsAttribute(clazz, bootstrapMethodsAttribute); + } + } + + + 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/NonEmptyAttributeFilter.java b/src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java new file mode 100644 index 000000000..2ccc09c1a --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java @@ -0,0 +1,293 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 is not empty. For instance, a local variable + * table without variables is empty. + * + * @author Eric Lafortune + */ +public class NonEmptyAttributeFilter +implements AttributeVisitor +{ + private final AttributeVisitor attributeVisitor; + + + /** + * Creates a new NonEmptyAttributeFilter. + * @param attributeVisitor the AttributeVisitor to which + * visits will be delegated. + */ + public NonEmptyAttributeFilter(AttributeVisitor attributeVisitor) + { + this.attributeVisitor = attributeVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + unknownAttribute.accept(clazz, attributeVisitor); + } + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + if (bootstrapMethodsAttribute.u2bootstrapMethodsCount > 0) + { + bootstrapMethodsAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + sourceFileAttribute.accept(clazz, attributeVisitor); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + sourceDirAttribute.accept(clazz, attributeVisitor); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + if (innerClassesAttribute.u2classesCount > 0) + { + innerClassesAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + enclosingMethodAttribute.accept(clazz, attributeVisitor); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + deprecatedAttribute.accept(clazz, attributeVisitor); + } + + + public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) + { + deprecatedAttribute.accept(clazz, field, attributeVisitor); + } + + + public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) + { + deprecatedAttribute.accept(clazz, method, attributeVisitor); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + syntheticAttribute.accept(clazz, attributeVisitor); + } + + + public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) + { + syntheticAttribute.accept(clazz, field, attributeVisitor); + } + + + public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) + { + syntheticAttribute.accept(clazz, method, attributeVisitor); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + signatureAttribute.accept(clazz, attributeVisitor); + } + + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) + { + signatureAttribute.accept(clazz, field, attributeVisitor); + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + signatureAttribute.accept(clazz, method, attributeVisitor); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + constantValueAttribute.accept(clazz, field, attributeVisitor); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + if (exceptionsAttribute.u2exceptionIndexTableLength > 0) + { + exceptionsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.accept(clazz, method, attributeVisitor); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + if (stackMapAttribute.u2stackMapFramesCount > 0) + { + stackMapAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + if (stackMapTableAttribute.u2stackMapFramesCount > 0) + { + stackMapTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + if (lineNumberTableAttribute.u2lineNumberTableLength > 0) + { + lineNumberTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + if (localVariableTableAttribute.u2localVariableTableLength > 0) + { + localVariableTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + if (localVariableTypeTableAttribute.u2localVariableTypeTableLength > 0) + { + localVariableTypeTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (runtimeVisibleAnnotationsAttribute.u2annotationsCount > 0) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (runtimeVisibleAnnotationsAttribute.u2annotationsCount > 0) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, field, attributeVisitor); + } + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + if (runtimeVisibleAnnotationsAttribute.u2annotationsCount > 0) + { + runtimeVisibleAnnotationsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (runtimeInvisibleAnnotationsAttribute.u2annotationsCount > 0) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, attributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (runtimeInvisibleAnnotationsAttribute.u2annotationsCount > 0) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, field, attributeVisitor); + } + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + if (runtimeInvisibleAnnotationsAttribute.u2annotationsCount > 0) + { + runtimeInvisibleAnnotationsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + if (runtimeVisibleParameterAnnotationsAttribute.u2parametersCount > 0) + { + runtimeVisibleParameterAnnotationsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + if (runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount > 0) + { + runtimeInvisibleParameterAnnotationsAttribute.accept(clazz, method, attributeVisitor); + } + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + annotationDefaultAttribute.accept(clazz, method, attributeVisitor); + } +} diff --git a/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java b/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java new file mode 100644 index 000000000..58e4e40cb --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java @@ -0,0 +1,362 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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. + * + * Stack map attributes and stack map table attributes are treated as optional. + * + * @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 visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + if (requiredAttributeVisitor != null) + { + bootstrapMethodsAttribute.accept(clazz, requiredAttributeVisitor); + } + } + + + 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 (optionalAttributeVisitor != null) + { + stackMapTableAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor); + } + } + + + 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 000000000..b5e02e20d --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/StackSizeComputer.java @@ -0,0 +1,378 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassPrinter; +import proguard.classfile.attribute.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +import java.util.Arrays; + +/** + * 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 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 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 + { + Arrays.fill(evaluated, 0, codeLength, 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 at 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 000000000..056244af6 --- /dev/null +++ b/src/proguard/classfile/attribute/visitor/package.html @@ -0,0 +1,3 @@ + +This package contains visitors for attributes and their components. + diff --git a/src/proguard/classfile/constant/ClassConstant.java b/src/proguard/classfile/constant/ClassConstant.java new file mode 100644 index 000000000..bbd9b3145 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 {@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}. + */ + public Clazz referencedClass; + + /** + * An extra field pointing to the java.lang.Class Clazz object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}.. + */ + 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 000000000..b4168cedb --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..a4c64cfb0 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..d552d4798 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..1709fcb8a --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..5f0d7f945 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 000000000..52f185283 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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/InvokeDynamicConstant.java b/src/proguard/classfile/constant/InvokeDynamicConstant.java new file mode 100755 index 000000000..57474aa6d --- /dev/null +++ b/src/proguard/classfile/constant/InvokeDynamicConstant.java @@ -0,0 +1,148 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.*; +import proguard.classfile.visitor.*; + +/** + * This Constant represents an invoke dynamic constant in the constant pool. + * + * @author Eric Lafortune + */ +public class InvokeDynamicConstant extends Constant +{ + public int u2bootstrapMethodAttributeIndex; + public int u2nameAndTypeIndex; + + /** + * An extra field pointing to the Clazz objects referenced in the + * descriptor string. This field is filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}. + * References to primitive types are ignored. + */ + public Clazz[] referencedClasses; + + + /** + * Creates an uninitialized InvokeDynamicConstant. + */ + public InvokeDynamicConstant() + { + } + + + /** + * Creates a new InvokeDynamicConstant with the given bootstrap method + * and name-and-type indices. + * @param u2bootstrapMethodAttributeIndex the index of the bootstrap method + * entry in the bootstrap methods + * attribute. + * @param u2nameAndTypeIndex the index of the name and type + * entry in the constant pool. + * @param referencedClasses the classes referenced by the + * type. + */ + public InvokeDynamicConstant(int u2bootstrapMethodAttributeIndex, + int u2nameAndTypeIndex, + Clazz[] referencedClasses) + { + this.u2bootstrapMethodAttributeIndex = u2bootstrapMethodAttributeIndex; + this.u2nameAndTypeIndex = u2nameAndTypeIndex; + this.referencedClasses = referencedClasses; + } + + + /** + * Returns the index of the bootstrap method in the bootstrap methods + * attribute of the class. + */ + public int getBootstrapMethodAttributeIndex() + { + return u2bootstrapMethodAttributeIndex; + } + + /** + * Returns the name-and-type index. + */ + public int getNameAndTypeIndex() + { + return u2nameAndTypeIndex; + } + + /** + * Returns the method name. + */ + public String getName(Clazz clazz) + { + return clazz.getName(u2nameAndTypeIndex); + } + + /** + * Returns the method type. + */ + public String getType(Clazz clazz) + { + return clazz.getType(u2nameAndTypeIndex); + } + + + /** + * Lets the Clazz objects referenced in the descriptor 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); + } + } + } + } + + + /** + * Lets the bootstrap method handle constant accept the given visitor. + */ + public void bootstrapMethodHandleAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + new BootstrapMethodHandleTraveler(constantVisitor).visitInvokeDynamicConstant(clazz, this); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_InvokeDynamic; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitInvokeDynamicConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/LongConstant.java b/src/proguard/classfile/constant/LongConstant.java new file mode 100644 index 000000000..2416f017e --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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/MethodHandleConstant.java b/src/proguard/classfile/constant/MethodHandleConstant.java new file mode 100755 index 000000000..6cffd9a54 --- /dev/null +++ b/src/proguard/classfile/constant/MethodHandleConstant.java @@ -0,0 +1,124 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a method handle constant in the constant pool. + * + * @author Eric Lafortune + */ +public class MethodHandleConstant extends Constant +{ + public int u1referenceKind; + public int u2referenceIndex; + + + /** + * An extra field pointing to the java.lang.invoke.MethodHandle Clazz object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}.. + */ + public Clazz javaLangInvokeMethodHandleClass; + + + /** + * Creates an uninitialized MethodHandleConstant. + */ + public MethodHandleConstant() + { + } + + + /** + * Creates a new MethodHandleConstant with the given type and method ref + * index. + * @param u1referenceKind the reference kind. + * @param u2referenceIndex the index of the field ref constant, interface + * method ref constant, or method ref constant in + * the constant pool. + */ + public MethodHandleConstant(int u1referenceKind, int u2referenceIndex) + { + this.u1referenceKind = u1referenceKind; + this.u2referenceIndex = u2referenceIndex; + } + + + /** + * Returns the kind of reference to which this constant is pointing. + * @return One of + * {@link ClassConstants#REF_getField }, + * {@link ClassConstants#REF_getStatic }, + * {@link ClassConstants#REF_putField }, + * {@link ClassConstants#REF_putStatic }, + * {@link ClassConstants#REF_invokeVirtual }, + * {@link ClassConstants#REF_invokeStatic }, + * {@link ClassConstants#REF_invokeSpecial }, + * {@link ClassConstants#REF_newInvokeSpecial}, or + * {@link ClassConstants#REF_invokeInterface }. + */ + public int getReferenceKind() + { + return u1referenceKind; + } + + /** + * Returns the field ref, interface method ref, or method ref index. + */ + public int getReferenceIndex() + { + return u2referenceIndex; + } + + + /** + * Returns the method/field name. + */ + public String getName(Clazz clazz) + { + return clazz.getRefName(u2referenceIndex); + } + + /** + * Returns the type. + */ + public String getType(Clazz clazz) + { + return clazz.getRefType(u2referenceIndex); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_MethodHandle; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitMethodHandleConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/MethodTypeConstant.java b/src/proguard/classfile/constant/MethodTypeConstant.java new file mode 100644 index 000000000..96c136ff2 --- /dev/null +++ b/src/proguard/classfile/constant/MethodTypeConstant.java @@ -0,0 +1,93 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a method handle constant in the constant pool. + * + * @author Eric Lafortune + */ +public class MethodTypeConstant extends Constant +{ + public int u2descriptorIndex; + + + /** + * An extra field pointing to the java.lang.invoke.MethodType Clazz object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}.. + */ + public Clazz javaLangInvokeMethodTypeClass; + + + /** + * Creates an uninitialized MethodTypeConstant. + */ + public MethodTypeConstant() + { + } + + + /** + * Creates a new MethodTypeConstant with the given descriptor index. + * @param u2descriptorIndex the index of the descriptor in the constant + * pool. + */ + public MethodTypeConstant(int u2descriptorIndex) + { + this.u2descriptorIndex = u2descriptorIndex; + } + + + /** + * Returns the descriptor index. + */ + public int getDescriptorIndex() + { + return u2descriptorIndex; + } + + + /** + * Returns the type. + */ + public String getType(Clazz clazz) + { + return clazz.getString(u2descriptorIndex); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_MethodType; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitMethodTypeConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/MethodrefConstant.java b/src/proguard/classfile/constant/MethodrefConstant.java new file mode 100644 index 000000000..1ffa23671 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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/constant/NameAndTypeConstant.java b/src/proguard/classfile/constant/NameAndTypeConstant.java new file mode 100644 index 000000000..2a4dd66a3 --- /dev/null +++ b/src/proguard/classfile/constant/NameAndTypeConstant.java @@ -0,0 +1,119 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Constant represents a name and type constant in the constant pool. + * + * @author Eric Lafortune + */ +public class NameAndTypeConstant extends Constant +{ + public int u2nameIndex; + public int u2descriptorIndex; + + + /** + * Creates an uninitialized NameAndTypeConstant. + */ + public NameAndTypeConstant() + { + } + + + /** + * 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 NameAndTypeConstant(int u2nameIndex, + int u2descriptorIndex) + { + this.u2nameIndex = u2nameIndex; + this.u2descriptorIndex = u2descriptorIndex; + } + + + /** + * Returns the name index. + */ + protected int getNameIndex() + { + return u2nameIndex; + } + + /** + * Sets the name index. + */ + protected void setNameIndex(int index) + { + u2nameIndex = index; + } + + /** + * Returns the descriptor index. + */ + protected int getDescriptorIndex() + { + return u2descriptorIndex; + } + + /** + * Sets the descriptor index. + */ + protected void setDescriptorIndex(int index) + { + u2descriptorIndex = index; + } + + /** + * Returns the name. + */ + public String getName(Clazz clazz) + { + return clazz.getString(u2nameIndex); + } + + /** + * Returns the type. + */ + public String getType(Clazz clazz) + { + return clazz.getString(u2descriptorIndex); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_NameAndType; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitNameAndTypeConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/RefConstant.java b/src/proguard/classfile/constant/RefConstant.java new file mode 100644 index 000000000..0a8fb78be --- /dev/null +++ b/src/proguard/classfile/constant/RefConstant.java @@ -0,0 +1,130 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.*; +import proguard.classfile.visitor.*; + +/** + * This Constant represents a ref constant in the constant pool. + * + * @author Eric Lafortune + */ +public abstract class RefConstant extends Constant +{ + public int u2classIndex; + public int u2nameAndTypeIndex; + + /** + * An extra field pointing to the referenced Clazz object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + */ + public Clazz referencedClass; + + /** + * An extra field optionally pointing to the referenced Member object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}. + */ + public Member referencedMember; + + + protected RefConstant() + { + } + + + /** + * Returns the class index. + */ + public int getClassIndex() + { + return u2classIndex; + } + + /** + * Returns the name-and-type index. + */ + public int getNameAndTypeIndex() + { + return u2nameAndTypeIndex; + } + + /** + * Sets the name-and-type index. + */ + public void setNameAndTypeIndex(int index) + { + u2nameAndTypeIndex = index; + } + + /** + * 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 referencedMemberAccept(MemberVisitor memberVisitor) + { + if (referencedMember != null) + { + referencedMember.accept(referencedClass, + memberVisitor); + } + } +} diff --git a/src/proguard/classfile/constant/StringConstant.java b/src/proguard/classfile/constant/StringConstant.java new file mode 100644 index 000000000..9b8374538 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +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 {@link + * proguard.classfile.util.DynamicClassReferenceInitializer + * DynamicClassReferenceInitializer} or by the {@link + * proguard.classfile.util.DynamicMemberReferenceInitializer + * DynamicMemberReferenceInitializer}. + */ + 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 {@link + * proguard.classfile.util.DynamicMemberReferenceInitializer + * DynamicMemberReferenceInitializer}. + */ + public Member referencedMember; + + /** + * An extra field pointing to the java.lang.String Clazz object. + * This field is typically filled out by the {@link + * proguard.classfile.util.ClassReferenceInitializer + * ClassReferenceInitializer}.. + */ + 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/constant/Utf8Constant.java b/src/proguard/classfile/constant/Utf8Constant.java new file mode 100644 index 000000000..3707ba98f --- /dev/null +++ b/src/proguard/classfile/constant/Utf8Constant.java @@ -0,0 +1,285 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +import java.io.UnsupportedEncodingException; + +/** + * This Constant represents a UTF-8 constant in the constant pool. + * + * @author Eric Lafortune + */ +public class Utf8Constant extends Constant +{ + private static final char TWO_BYTE_LIMIT = 0x80; + private static final int TWO_BYTE_CONSTANT1 = 0xc0; + private static final int TWO_BYTE_CONSTANT2 = 0x80; + private static final int TWO_BYTE_SHIFT1 = 6; + private static final int TWO_BYTE_MASK1 = 0x1f; + private static final int TWO_BYTE_MASK2 = 0x3f; + + private static final char THREE_BYTE_LIMIT = 0x800; + private static final int THREE_BYTE_CONSTANT1 = 0xe0; + private static final int THREE_BYTE_CONSTANT2 = 0x80; + private static final int THREE_BYTE_CONSTANT3 = 0x80; + private static final int THREE_BYTE_SHIFT1 = 12; + private static final int THREE_BYTE_SHIFT2 = 6; + private static final int THREE_BYTE_MASK1 = 0x0f; + private static final int THREE_BYTE_MASK2 = 0x3f; + private static final int THREE_BYTE_MASK3 = 0x3f; + + + // There are a lot of Utf8Constant objects, so we're optimising their storage. + // 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. + + //private int u2length; + private byte[] bytes; + + private String string; + + + /** + * Creates an uninitialized Utf8Constant. + * + */ + public Utf8Constant() + { + } + + + /** + * Creates a Utf8Constant containing the given string. + */ + public Utf8Constant(String string) + { + this.bytes = null; + this.string = string; + } + + + /** + * Initializes the UTF-8 data with an array of bytes. + */ + public void setBytes(byte[] bytes) + { + this.bytes = bytes; + this.string = null; + } + + + /** + * Returns the UTF-8 data as an array of bytes. + */ + public byte[] getBytes() + { + try + { + switchToByteArrayRepresentation(); + } + catch (UnsupportedEncodingException ex) + { + throw new RuntimeException(ex.getMessage()); + } + + return bytes; + } + + + /** + * Initializes the UTF-8 data with a String. + */ + public void setString(String utf8String) + { + this.bytes = null; + this.string = utf8String; + } + + + /** + * Returns the UTF-8 data as a String. + */ + public String getString() + { + try + { + switchToStringRepresentation(); + } + catch (UnsupportedEncodingException ex) + { + throw new RuntimeException(ex.getMessage()); + } + + return string; + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Utf8; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitUtf8Constant(clazz, this); + } + + + // Small utility methods. + + /** + * Switches to a byte array representation of the UTF-8 data. + */ + private void switchToByteArrayRepresentation() throws UnsupportedEncodingException + { + if (bytes == null) + { + bytes = getByteArrayRepresentation(string); + string = null; + } + } + + + /** + * Switches to a String representation of the UTF-8 data. + */ + private void switchToStringRepresentation() throws UnsupportedEncodingException + { + if (string == null) + { + string = getStringRepresentation(bytes); + bytes = null; + } + } + + + /** + * Returns the modified UTF-8 byte array representation of the given string. + */ + private byte[] getByteArrayRepresentation(String string) throws UnsupportedEncodingException + { + // We're computing the byte array ourselves, because the implementation + // of String.getBytes("UTF-8") has a bug, at least up to JRE 1.4.2. + // Also note the special treatment of the 0 character. + + // Compute the byte array length. + int byteLength = 0; + int stringLength = string.length(); + for (int stringIndex = 0; stringIndex < stringLength; stringIndex++) + { + char c = string.charAt(stringIndex); + + // The character is represented by one, two, or three bytes. + byteLength += c == 0 ? 2 : + c < TWO_BYTE_LIMIT ? 1 : + c < THREE_BYTE_LIMIT ? 2 : + 3; + } + + // Allocate the byte array with the computed length. + byte[] bytes = new byte[byteLength]; + + // Fill out the array. + int byteIndex = 0; + for (int stringIndex = 0; stringIndex < stringLength; stringIndex++) + { + char c = string.charAt(stringIndex); + if (c == 0) + { + // The 0 character gets a two-byte representation in classes. + bytes[byteIndex++] = (byte)TWO_BYTE_CONSTANT1; + bytes[byteIndex++] = (byte)TWO_BYTE_CONSTANT2; + } + else if (c < TWO_BYTE_LIMIT) + { + // The character is represented by a single byte. + bytes[byteIndex++] = (byte)c; + } + else if (c < THREE_BYTE_LIMIT) + { + // The character is represented by two bytes. + bytes[byteIndex++] = (byte)(TWO_BYTE_CONSTANT1 | ((c >>> TWO_BYTE_SHIFT1) & TWO_BYTE_MASK1)); + bytes[byteIndex++] = (byte)(TWO_BYTE_CONSTANT2 | ( c & TWO_BYTE_MASK2)); + } + else + { + // The character is represented by three bytes. + bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT1 | ((c >>> THREE_BYTE_SHIFT1) & THREE_BYTE_MASK1)); + bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT2 | ((c >>> THREE_BYTE_SHIFT2) & THREE_BYTE_MASK2)); + bytes[byteIndex++] = (byte)(THREE_BYTE_CONSTANT3 | ( c & THREE_BYTE_MASK3)); + } + } + + return bytes; + } + + + /** + * Returns the String representation of the given modified UTF-8 byte array. + */ + private String getStringRepresentation(byte[] bytes) throws UnsupportedEncodingException + { + // We're computing the string ourselves, because the implementation + // of "new String(bytes)" doesn't honor the special treatment of + // the 0 character in JRE 1.6_u11. + + // Allocate the byte array with the computed length. + char[] chars = new char[bytes.length]; + + // Fill out the array. + int charIndex = 0; + int byteIndex = 0; + while (byteIndex < bytes.length) + { + + int b = bytes[byteIndex++] & 0xff; + + // Depending on the flag bits in the first byte, the character + // is represented by a single byte, by two bytes, or by three + // bytes. We're not checking the redundant flag bits in the + // second byte and the third byte. + try + { + chars[charIndex++] = + (char)(b < TWO_BYTE_CONSTANT1 ? b : + + b < THREE_BYTE_CONSTANT1 ? ((b & TWO_BYTE_MASK1) << TWO_BYTE_SHIFT1) | + ((bytes[byteIndex++] & TWO_BYTE_MASK2) ) : + + ((b & THREE_BYTE_MASK1) << THREE_BYTE_SHIFT1) | + ((bytes[byteIndex++] & THREE_BYTE_MASK2) << THREE_BYTE_SHIFT2) | + ((bytes[byteIndex++] & THREE_BYTE_MASK3) )); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new UnsupportedEncodingException("Missing UTF-8 bytes after initial byte [0x"+Integer.toHexString(b)+"] in string ["+new String(chars, 0, charIndex)+"]"); + } + } + + return new String(chars, 0, charIndex); + } +} diff --git a/src/proguard/classfile/constant/visitor/AllConstantVisitor.java b/src/proguard/classfile/constant/visitor/AllConstantVisitor.java new file mode 100644 index 000000000..3c3660921 --- /dev/null +++ b/src/proguard/classfile/constant/visitor/AllConstantVisitor.java @@ -0,0 +1,53 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.visitor.ClassVisitor; + + +/** + * This ClassVisitor lets a given ConstantVisitor visit all constant pool + * entries of the program classes it visits. + * + * @author Eric Lafortune + */ +public class AllConstantVisitor implements ClassVisitor +{ + private final ConstantVisitor constantVisitor; + + + public AllConstantVisitor(ConstantVisitor constantVisitor) + { + this.constantVisitor = constantVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.constantPoolEntriesAccept(constantVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) {} +} diff --git a/src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java b/src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java new file mode 100644 index 000000000..6dce6c567 --- /dev/null +++ b/src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java @@ -0,0 +1,100 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; +import proguard.optimize.info.MethodOptimizationInfo; + +/** + * This ConstantVisitor and BootstrapMethodInfoVisitor travels from any invoke + * dynamic constants or bootstrap method info entries that it visits to their + * bootstrap method handle constants, and applies a given constant visitor. + * + * @author Eric Lafortune + */ +public class BootstrapMethodHandleTraveler +extends SimplifiedVisitor +implements ConstantVisitor, + AttributeVisitor, + BootstrapMethodInfoVisitor +{ + private ConstantVisitor bootstrapMethodHandleVisitor; + + // Field serving as a method argument. + int bootstrapMethodAttributeIndex; + + + /** + * Creates a new BootstrapMethodHandleVisitor that will delegate to the + * given constant visitor. + */ + public BootstrapMethodHandleTraveler(ConstantVisitor bootstrapMethodHandleVisitor) + { + this.bootstrapMethodHandleVisitor = bootstrapMethodHandleVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // Pass the method index. + bootstrapMethodAttributeIndex = + invokeDynamicConstant.u2bootstrapMethodAttributeIndex; + + // Delegate to the bootstrap method. + clazz.attributesAccept(this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + // Check bootstrap methods. + bootstrapMethodsAttribute.bootstrapMethodEntryAccept(clazz, + bootstrapMethodAttributeIndex, + this); + } + + + // Implementations for BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + // Check bootstrap method. + clazz.constantPoolEntryAccept(bootstrapMethodInfo.u2methodHandleIndex, + bootstrapMethodHandleVisitor); + } +} diff --git a/src/proguard/classfile/constant/visitor/ConstantTagFilter.java b/src/proguard/classfile/constant/visitor/ConstantTagFilter.java new file mode 100644 index 000000000..a3fcc8ab7 --- /dev/null +++ b/src/proguard/classfile/constant/visitor/ConstantTagFilter.java @@ -0,0 +1,86 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor delegates its visits to one or more + * specified types of constants. + * + * @author Eric Lafortune + */ +public class ConstantTagFilter +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final int constantTagMask; + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ConstantTagFilter. + * @param constantTag the type of constants for which visits will be + * delegated. + * @param constantVisitor the ConstantVisitor to which visits + * will be delegated. + */ + public ConstantTagFilter(int constantTag, + ConstantVisitor constantVisitor) + { + this.constantTagMask = 1 << constantTag; + this.constantVisitor = constantVisitor; + } + + + /** + * Creates a new ConstantTagFilter. + * @param constantTags the types of constants for which visits will be + * delegated. + * @param constantVisitor the ConstantVisitor to which visits + * will be delegated. + */ + public ConstantTagFilter(int[] constantTags, + ConstantVisitor constantVisitor) + { + int constantTagMask = 0; + for (int index = 0; index < constantTags.length; index++) + { + constantTagMask |= 1 << constantTags[index]; + } + + this.constantTagMask = constantTagMask; + this.constantVisitor = constantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + if (((1 << constant.getTag()) & constantTagMask) != 0) + { + constant.accept(clazz, constantVisitor); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/constant/visitor/ConstantVisitor.java b/src/proguard/classfile/constant/visitor/ConstantVisitor.java new file mode 100644 index 000000000..362d54d9d --- /dev/null +++ b/src/proguard/classfile/constant/visitor/ConstantVisitor.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 Constant + * 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 visitInvokeDynamicConstant( Clazz clazz, InvokeDynamicConstant invokeDynamicConstant); + public void visitMethodHandleConstant( Clazz clazz, MethodHandleConstant methodHandleConstant); + 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 visitMethodTypeConstant( Clazz clazz, MethodTypeConstant methodTypeConstant); + public void visitNameAndTypeConstant( Clazz clazz, NameAndTypeConstant nameAndTypeConstant); +} diff --git a/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java b/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java new file mode 100644 index 000000000..bff4d1e31 --- /dev/null +++ b/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.editor.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor delegates its visits to class constants + * to another given ConstantVisitor, except for one given class. + * + * @author Eric Lafortune + */ +public class ExceptClassConstantFilter +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final String exceptClassName; + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ExceptClassConstantFilter. + * @param exceptClassName the name of the class that will not be visited. + * @param constantVisitor the ConstantVisitor to which visits + * will be delegated. + */ + public ExceptClassConstantFilter(String exceptClassName, + ConstantVisitor constantVisitor) + { + this.exceptClassName = exceptClassName; + this.constantVisitor = constantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + if (!classConstant.getName(clazz).equals(exceptClassName)) + { + constantVisitor.visitClassConstant(clazz, classConstant); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/constant/visitor/MethodrefTraveler.java b/src/proguard/classfile/constant/visitor/MethodrefTraveler.java new file mode 100644 index 000000000..acd1bc84b --- /dev/null +++ b/src/proguard/classfile/constant/visitor/MethodrefTraveler.java @@ -0,0 +1,60 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.attribute.*; +import proguard.classfile.constant.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor travels from any method handle constants that it visits + * to their methodref constants, and applies a given constant visitor. + * + * @author Eric Lafortune + */ +public class MethodrefTraveler +extends SimplifiedVisitor +implements ConstantVisitor +{ + private ConstantVisitor methodrefConstantVisitor; + + + /** + * Creates a new v that will delegate to the given constant visitor. + */ + public MethodrefTraveler(ConstantVisitor methodrefConstantVisitor) + { + this.methodrefConstantVisitor = methodrefConstantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, + methodrefConstantVisitor); + } +} diff --git a/src/proguard/classfile/constant/visitor/package.html b/src/proguard/classfile/constant/visitor/package.html new file mode 100644 index 000000000..e20f48e51 --- /dev/null +++ b/src/proguard/classfile/constant/visitor/package.html @@ -0,0 +1,3 @@ + +This package contains visitors for class constants. + diff --git a/src/proguard/classfile/editor/AccessFixer.java b/src/proguard/classfile/editor/AccessFixer.java new file mode 100644 index 000000000..d77053137 --- /dev/null +++ b/src/proguard/classfile/editor/AccessFixer.java @@ -0,0 +1,180 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.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 MyReferencedClassFinder referencedClassFinder = new MyReferencedClassFinder(); + + private Clazz referencingClass; + private Clazz referencedClass; + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + referencingClass = clazz; + referencedClass = stringConstant.referencedClass; + + // Make sure the access flags of the referenced class or class member, + // if any, are acceptable. + stringConstant.referencedClassAccept(this); + stringConstant.referencedMemberAccept(this); + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // Check the bootstrap method. + invokeDynamicConstant.bootstrapMethodHandleAccept(clazz, this); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + // Check the method reference. + clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, this); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + referencingClass = clazz; + + // Remember the specified class, since it might be different from + // the referenced class that actually contains the class member. + clazz.constantPoolEntryAccept(refConstant.u2classIndex, referencedClassFinder); + + // 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 = + inSamePackage(programClass, referencingClass) ? AccessUtil.PACKAGE_VISIBLE : + 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 : + referencedClass.extends_(referencingClass) && + referencingClass.extends_(programClass) ? AccessUtil.PROTECTED : + AccessUtil.PUBLIC; + + // Fix the class member access flags if necessary. + if (currentAccessLevel < requiredAccessLevel) + { + programMember.u2accessFlags = + AccessUtil.replaceAccessFlags(currentAccessFlags, + AccessUtil.accessFlags(requiredAccessLevel)); + } + } + + + /** + * This ConstantVisitor returns the referenced class of the class constant + * that it visits. + */ + private class MyReferencedClassFinder + extends SimplifiedVisitor + implements ConstantVisitor + { + // Implementations for ConstantVisitor. + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + referencedClass = classConstant.referencedClass; + } + } + + + // 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/AnnotationAdder.java b/src/proguard/classfile/editor/AnnotationAdder.java new file mode 100644 index 000000000..0389ab112 --- /dev/null +++ b/src/proguard/classfile/editor/AnnotationAdder.java @@ -0,0 +1,153 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.*; +import proguard.classfile.attribute.annotation.visitor.AnnotationVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AnnotationVisitor adds all annotations that it visits to the given + * target annotation element value, target annotation attribute, or target + * parameter annotation attribute. + * + * @author Eric Lafortune + */ +public class AnnotationAdder +extends SimplifiedVisitor +implements AnnotationVisitor +{ + private static final ElementValue[] EMPTY_ELEMENT_VALUES = new ElementValue[0]; + + + private final ProgramClass targetClass; + private final AnnotationElementValue targetAnnotationElementValue; + private final AnnotationsAttributeEditor annotationsAttributeEditor; + private final ParameterAnnotationsAttributeEditor parameterAnnotationsAttributeEditor; + + private final ConstantAdder constantAdder; + + + /** + * Creates a new AnnotationAdder that will copy annotations into the given + * target annotation element value. + */ + public AnnotationAdder(ProgramClass targetClass, + AnnotationElementValue targetAnnotationElementValue) + { + this.targetClass = targetClass; + this.targetAnnotationElementValue = targetAnnotationElementValue; + this.annotationsAttributeEditor = null; + this.parameterAnnotationsAttributeEditor = null; + + constantAdder = new ConstantAdder(targetClass); + } + + + /** + * Creates a new AnnotationAdder that will copy annotations into the given + * target annotations attribute. + */ + public AnnotationAdder(ProgramClass targetClass, + AnnotationsAttribute targetAnnotationsAttribute) + { + this.targetClass = targetClass; + this.targetAnnotationElementValue = null; + this.annotationsAttributeEditor = new AnnotationsAttributeEditor(targetAnnotationsAttribute); + this.parameterAnnotationsAttributeEditor = null; + + constantAdder = new ConstantAdder(targetClass); + } + + + /** + * Creates a new AnnotationAdder that will copy annotations into the given + * target parameter annotations attribute. + */ + public AnnotationAdder(ProgramClass targetClass, + ParameterAnnotationsAttribute targetParameterAnnotationsAttribute) + { + this.targetClass = targetClass; + this.targetAnnotationElementValue = null; + this.annotationsAttributeEditor = null; + this.parameterAnnotationsAttributeEditor = new ParameterAnnotationsAttributeEditor(targetParameterAnnotationsAttribute); + + constantAdder = new ConstantAdder(targetClass); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + Annotation newAnnotation = + new Annotation(constantAdder.addConstant(clazz, annotation.u2typeIndex), + 0, + annotation.u2elementValuesCount > 0 ? + new ElementValue[annotation.u2elementValuesCount] : + EMPTY_ELEMENT_VALUES); + + // TODO: Clone array. + newAnnotation.referencedClasses = annotation.referencedClasses; + + // Add the element values. + annotation.elementValuesAccept(clazz, + new ElementValueAdder(targetClass, + newAnnotation, + false)); + + // What's the target? + if (targetAnnotationElementValue != null) + { + // Simply set the completed annotation. + targetAnnotationElementValue.annotationValue = newAnnotation; + } + else + { + // Add the completed annotation. + annotationsAttributeEditor.addAnnotation(newAnnotation); + } + } + + + public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation) + { + Annotation newAnnotation = + new Annotation(constantAdder.addConstant(clazz, annotation.u2typeIndex), + 0, + annotation.u2elementValuesCount > 0 ? + new ElementValue[annotation.u2elementValuesCount] : + EMPTY_ELEMENT_VALUES); + + // TODO: Clone array. + newAnnotation.referencedClasses = annotation.referencedClasses; + + // Add the element values. + annotation.elementValuesAccept(clazz, + new ElementValueAdder(targetClass, + newAnnotation, + false)); + + // Add the completed annotation. + parameterAnnotationsAttributeEditor.addAnnotation(parameterIndex, newAnnotation); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/AnnotationsAttributeEditor.java b/src/proguard/classfile/editor/AnnotationsAttributeEditor.java new file mode 100644 index 000000000..a175c3319 --- /dev/null +++ b/src/proguard/classfile/editor/AnnotationsAttributeEditor.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.attribute.annotation.*; + +/** + * This class can add annotations to a given annotations attribute. + * Annotations to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class AnnotationsAttributeEditor +{ + private AnnotationsAttribute targetAnnotationsAttribute; + + + /** + * Creates a new AnnotationsAttributeEditor that will edit annotations in + * the given annotations attribute. + */ + public AnnotationsAttributeEditor(AnnotationsAttribute targetAnnotationsAttribute) + { + this.targetAnnotationsAttribute = targetAnnotationsAttribute; + } + + + /** + * Adds a given annotation to the annotations attribute. + */ + public void addAnnotation(Annotation annotation) + { + int annotationsCount = targetAnnotationsAttribute.u2annotationsCount; + Annotation[] annotations = targetAnnotationsAttribute.annotations; + + // Make sure there is enough space for the new annotation. + if (annotations.length <= annotationsCount) + { + targetAnnotationsAttribute.annotations = new Annotation[annotationsCount+1]; + System.arraycopy(annotations, 0, + targetAnnotationsAttribute.annotations, 0, + annotationsCount); + annotations = targetAnnotationsAttribute.annotations; + } + + // Add the annotation. + annotations[targetAnnotationsAttribute.u2annotationsCount++] = annotation; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/AttributeAdder.java b/src/proguard/classfile/editor/AttributeAdder.java new file mode 100644 index 000000000..ad4ecc0af --- /dev/null +++ b/src/proguard/classfile/editor/AttributeAdder.java @@ -0,0 +1,455 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor adds all attributes that it visits to the given + * target class, class member, or attribute. + * + * @author Eric Lafortune + */ +public class AttributeAdder +extends SimplifiedVisitor +implements AttributeVisitor +{ + private static final byte[] EMPTY_BYTES = new byte[0]; + private static final int[] EMPTY_INTS = new int[0]; + private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; + private static final ExceptionInfo[] EMPTY_EXCEPTIONS = new ExceptionInfo[0]; + + + private final ProgramClass targetClass; + private final ProgramMember targetMember; + private final CodeAttribute targetCodeAttribute; + private final boolean replaceAttributes; + + private final ConstantAdder constantAdder; + private final AttributesEditor attributesEditor; + + + /** + * Creates a new AttributeAdder that will copy attributes into the given + * target class. + */ + public AttributeAdder(ProgramClass targetClass, + boolean replaceAttributes) + { + this(targetClass, null, null, replaceAttributes); + } + + + /** + * Creates a new AttributeAdder that will copy attributes into the given + * target class member. + */ + public AttributeAdder(ProgramClass targetClass, + ProgramMember targetMember, + boolean replaceAttributes) + { + this(targetClass, targetMember, null, replaceAttributes); + } + + + /** + * Creates a new AttributeAdder that will copy attributes into the given + * target attribute. + */ + public AttributeAdder(ProgramClass targetClass, + ProgramMember targetMember, + CodeAttribute targetCodeAttribute, + boolean replaceAttributes) + { + this.targetClass = targetClass; + this.targetMember = targetMember; + this.targetCodeAttribute = targetCodeAttribute; + this.replaceAttributes = replaceAttributes; + + constantAdder = new ConstantAdder(targetClass); + attributesEditor = new AttributesEditor(targetClass, + targetMember, + targetCodeAttribute, + replaceAttributes); + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + // Create a copy of the attribute. + UnknownAttribute newUnknownAttribute = + new UnknownAttribute(constantAdder.addConstant(clazz, unknownAttribute.u2attributeNameIndex), + unknownAttribute.u4attributeLength, + unknownAttribute.info); + + // Add it to the target class. + attributesEditor.addAttribute(newUnknownAttribute); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + // Create a copy of the attribute. + SourceFileAttribute newSourceFileAttribute = + new SourceFileAttribute(constantAdder.addConstant(clazz, sourceFileAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, sourceFileAttribute.u2sourceFileIndex)); + + // Add it to the target class. + attributesEditor.addAttribute(newSourceFileAttribute); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + // Create a copy of the attribute. + SourceDirAttribute newSourceDirAttribute = + new SourceDirAttribute(constantAdder.addConstant(clazz, sourceDirAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, sourceDirAttribute.u2sourceDirIndex)); + + // Add it to the target class. + attributesEditor.addAttribute(newSourceDirAttribute); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Create a copy of the attribute. + InnerClassesAttribute newInnerClassesAttribute = + new InnerClassesAttribute(constantAdder.addConstant(clazz, innerClassesAttribute.u2attributeNameIndex), + 0, + null); + + // Add it to the target class. + attributesEditor.addAttribute(newInnerClassesAttribute); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + // Create a copy of the attribute. + EnclosingMethodAttribute newEnclosingMethodAttribute = + new EnclosingMethodAttribute(constantAdder.addConstant(clazz, enclosingMethodAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, enclosingMethodAttribute.u2classIndex), + enclosingMethodAttribute.u2nameAndTypeIndex == 0 ? 0 : + constantAdder.addConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex)); + + newEnclosingMethodAttribute.referencedClass = enclosingMethodAttribute.referencedClass; + newEnclosingMethodAttribute.referencedMethod = enclosingMethodAttribute.referencedMethod; + + // Add it to the target class. + attributesEditor.addAttribute(newEnclosingMethodAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + // Create a copy of the attribute. + DeprecatedAttribute newDeprecatedAttribute = + new DeprecatedAttribute(constantAdder.addConstant(clazz, deprecatedAttribute.u2attributeNameIndex)); + + // Add it to the target. + attributesEditor.addAttribute(newDeprecatedAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + // Create a copy of the attribute. + SyntheticAttribute newSyntheticAttribute = + new SyntheticAttribute(constantAdder.addConstant(clazz, syntheticAttribute.u2attributeNameIndex)); + + // Add it to the target. + attributesEditor.addAttribute(newSyntheticAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Create a copy of the attribute. + SignatureAttribute newSignatureAttribute = + new SignatureAttribute(constantAdder.addConstant(clazz, signatureAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, signatureAttribute.u2signatureIndex)); + + newSignatureAttribute.referencedClasses = signatureAttribute.referencedClasses; + + // Add it to the target. + attributesEditor.addAttribute(newSignatureAttribute); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + // Create a copy of the attribute. + ConstantValueAttribute newConstantValueAttribute = + new ConstantValueAttribute(constantAdder.addConstant(clazz, constantValueAttribute.u2attributeNameIndex), + constantAdder.addConstant(clazz, constantValueAttribute.u2constantValueIndex)); + + // Add it to the target field. + attributesEditor.addAttribute(newConstantValueAttribute); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + // Create a new exceptions attribute. + ExceptionsAttribute newExceptionsAttribute = + new ExceptionsAttribute(constantAdder.addConstant(clazz, exceptionsAttribute.u2attributeNameIndex), + 0, + exceptionsAttribute.u2exceptionIndexTableLength > 0 ? + new int[exceptionsAttribute.u2exceptionIndexTableLength] : + EMPTY_INTS); + + // Add the exceptions. + exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, + new ExceptionAdder(targetClass, + newExceptionsAttribute)); + + // Add it to the target method. + attributesEditor.addAttribute(newExceptionsAttribute); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Create a new code attribute. + CodeAttribute newCodeAttribute = + new CodeAttribute(constantAdder.addConstant(clazz, codeAttribute.u2attributeNameIndex), + codeAttribute.u2maxStack, + codeAttribute.u2maxLocals, + 0, + EMPTY_BYTES, + 0, + codeAttribute.u2exceptionTableLength > 0 ? + new ExceptionInfo[codeAttribute.u2exceptionTableLength] : + EMPTY_EXCEPTIONS, + 0, + codeAttribute.u2attributesCount > 0 ? + new Attribute[codeAttribute.u2attributesCount] : + EMPTY_ATTRIBUTES); + + CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); + + codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength + 32); + + // Add the instructions. + codeAttribute.instructionsAccept(clazz, + method, + new InstructionAdder(targetClass, + codeAttributeComposer)); + + // Append a label just after the code. + codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); + + // Add the exceptions. + codeAttribute.exceptionsAccept(clazz, + method, + new ExceptionInfoAdder(targetClass, + codeAttributeComposer)); + + codeAttributeComposer.endCodeFragment(); + + // Add the attributes. + codeAttribute.attributesAccept(clazz, + method, + new AttributeAdder(targetClass, + targetMember, + newCodeAttribute, + replaceAttributes)); + + // Apply these changes to the new code attribute. + codeAttributeComposer.visitCodeAttribute(targetClass, + (Method)targetMember, + newCodeAttribute); + + // Add the completed code attribute to the target method. + attributesEditor.addAttribute(newCodeAttribute); + } + + + 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) + { + // Create a new line number table attribute. + LineNumberTableAttribute newLineNumberTableAttribute = + new LineNumberTableAttribute(constantAdder.addConstant(clazz, lineNumberTableAttribute.u2attributeNameIndex), + 0, + new LineNumberInfo[lineNumberTableAttribute.u2lineNumberTableLength]); + + // Add the line numbers. + lineNumberTableAttribute.lineNumbersAccept(clazz, + method, + codeAttribute, + new LineNumberInfoAdder(newLineNumberTableAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newLineNumberTableAttribute); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Create a new local variable table attribute. + LocalVariableTableAttribute newLocalVariableTableAttribute = + new LocalVariableTableAttribute(constantAdder.addConstant(clazz, localVariableTableAttribute.u2attributeNameIndex), + 0, + new LocalVariableInfo[localVariableTableAttribute.u2localVariableTableLength]); + + // Add the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, + method, + codeAttribute, + new LocalVariableInfoAdder(targetClass, newLocalVariableTableAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newLocalVariableTableAttribute); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Create a new local variable type table attribute. + LocalVariableTypeTableAttribute newLocalVariableTypeTableAttribute = + new LocalVariableTypeTableAttribute(constantAdder.addConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex), + 0, + new LocalVariableTypeInfo[localVariableTypeTableAttribute.u2localVariableTypeTableLength]); + + // Add the local variable types. + localVariableTypeTableAttribute.localVariablesAccept(clazz, + method, + codeAttribute, + new LocalVariableTypeInfoAdder(targetClass, newLocalVariableTypeTableAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newLocalVariableTypeTableAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + // Create a new annotations attribute. + RuntimeVisibleAnnotationsAttribute newAnnotationsAttribute = + new RuntimeVisibleAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeVisibleAnnotationsAttribute.u2attributeNameIndex), + 0, + new Annotation[runtimeVisibleAnnotationsAttribute.u2annotationsCount]); + + // Add the annotations. + runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, + new AnnotationAdder(targetClass, + newAnnotationsAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + // Create a new annotations attribute. + RuntimeInvisibleAnnotationsAttribute newAnnotationsAttribute = + new RuntimeInvisibleAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeInvisibleAnnotationsAttribute.u2attributeNameIndex), + 0, + new Annotation[runtimeInvisibleAnnotationsAttribute.u2annotationsCount]); + + // Add the annotations. + runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, + new AnnotationAdder(targetClass, + newAnnotationsAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newAnnotationsAttribute); + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + // Create a new annotations attribute. + RuntimeVisibleParameterAnnotationsAttribute newParameterAnnotationsAttribute = + new RuntimeVisibleParameterAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeVisibleParameterAnnotationsAttribute.u2attributeNameIndex), + 0, + new int[runtimeVisibleParameterAnnotationsAttribute.u2parametersCount], + new Annotation[runtimeVisibleParameterAnnotationsAttribute.u2parametersCount][]); + + // Add the annotations. + runtimeVisibleParameterAnnotationsAttribute.annotationsAccept(clazz, + method, + new AnnotationAdder(targetClass, + newParameterAnnotationsAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newParameterAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + // Create a new annotations attribute. + RuntimeInvisibleParameterAnnotationsAttribute newParameterAnnotationsAttribute = + new RuntimeInvisibleParameterAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeInvisibleParameterAnnotationsAttribute.u2attributeNameIndex), + 0, + new int[runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount], + new Annotation[runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount][]); + + // Add the annotations. + runtimeInvisibleParameterAnnotationsAttribute.annotationsAccept(clazz, + method, + new AnnotationAdder(targetClass, + newParameterAnnotationsAttribute)); + + // Add it to the target. + attributesEditor.addAttribute(newParameterAnnotationsAttribute); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Create a new annotation default attribute. + AnnotationDefaultAttribute newAnnotationDefaultAttribute = + new AnnotationDefaultAttribute(constantAdder.addConstant(clazz, annotationDefaultAttribute.u2attributeNameIndex), + null); + + // Add the annotations. + annotationDefaultAttribute.defaultValueAccept(clazz, + new ElementValueAdder(targetClass, + newAnnotationDefaultAttribute, + false)); + + // Add it to the target. + attributesEditor.addAttribute(newAnnotationDefaultAttribute); + } +} diff --git a/src/proguard/classfile/editor/AttributeSorter.java b/src/proguard/classfile/editor/AttributeSorter.java new file mode 100644 index 000000000..23fe02793 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; +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 000000000..f50b8f127 --- /dev/null +++ b/src/proguard/classfile/editor/AttributesEditor.java @@ -0,0 +1,269 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 +{ + private final ProgramClass targetClass; + private final ProgramMember targetMember; + private final CodeAttribute targetAttribute; + private final boolean replaceAttributes; + + + /** + * Creates a new AttributeAdder that will edit attributes in the given + * target class. + */ + public AttributesEditor(ProgramClass targetClass, + boolean replaceAttributes) + { + this(targetClass, null, null, replaceAttributes); + } + + + /** + * Creates a new AttributeAdder that will edit attributes in the given + * target class member. + */ + public AttributesEditor(ProgramClass targetClass, + ProgramMember targetMember, + boolean replaceAttributes) + { + this(targetClass, targetMember, null, replaceAttributes); + } + + + /** + * Creates a new AttributeAdder that will edit attributes in the given + * target code attribute. + */ + public AttributesEditor(ProgramClass targetClass, + ProgramMember targetMember, + CodeAttribute targetAttribute, + boolean replaceAttributes) + { + this.targetClass = targetClass; + this.targetMember = targetMember; + this.targetAttribute = targetAttribute; + this.replaceAttributes = replaceAttributes; + } + + + /** + * Adds the given attribute to the target. + */ + public void addAttribute(Attribute attribute) + { + // What's the target? + if (targetAttribute != null) + { + // Try to replace an existing attribute. + if (!replaceAttributes || + !replaceAttribute(targetAttribute.u2attributesCount, + targetAttribute.attributes, + attribute)) + { + // Otherwise append the attribute. + targetAttribute.attributes = + addAttribute(targetAttribute.u2attributesCount, + targetAttribute.attributes, + attribute); + + targetAttribute.u2attributesCount++; + } + } + else if (targetMember != null) + { + // Try to replace an existing attribute. + if (!replaceAttributes || + !replaceAttribute(targetMember.u2attributesCount, + targetMember.attributes, + attribute)) + { + // Otherwise append the attribute. + targetMember.attributes = + addAttribute(targetMember.u2attributesCount, + targetMember.attributes, + attribute); + + targetMember.u2attributesCount++; + } + } + else + { + // Try to replace an existing attribute. + if (!replaceAttributes || + !replaceAttribute(targetClass.u2attributesCount, + targetClass.attributes, + attribute)) + { + // Otherwise append the attribute. + targetClass.attributes = + addAttribute(targetClass.u2attributesCount, + targetClass.attributes, + attribute); + + targetClass.u2attributesCount++; + } + } + } + + + /** + * Deletes the specified attribute from the target. + */ + public void deleteAttribute(String attributeName) + { + // What's the target? + if (targetAttribute != null) + { + targetAttribute.u2attributesCount = + deleteAttribute(targetAttribute.u2attributesCount, + targetAttribute.attributes, + attributeName); + } + else if (targetMember != null) + { + targetMember.u2attributesCount = + deleteAttribute(targetMember.u2attributesCount, + targetMember.attributes, + attributeName); + } + else + { + targetClass.u2attributesCount = + deleteAttribute(targetClass.u2attributesCount, + targetClass.attributes, + attributeName); + } + } + + + // Small utility methods. + + /** + * Tries put the given attribute in place of an existing attribute of the + * same name, returning whether it was present. + */ + private boolean replaceAttribute(int attributesCount, + Attribute[] attributes, + Attribute attribute) + { + // Find the attribute with the same name. + int index = findAttribute(attributesCount, + attributes, + attribute.getAttributeName(targetClass)); + if (index < 0) + { + return false; + } + + attributes[index] = attribute; + + return true; + } + + + /** + * Appends the given attribute to the given array of attributes, creating a + * new array if necessary. + */ + private Attribute[] addAttribute(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 the attributes with the given name from the given array of + * attributes, returning the new number of attributes. + */ + private int deleteAttribute(int attributesCount, + Attribute[] attributes, + String attributeName) + { + // Find the attribute. + int index = findAttribute(attributesCount, + attributes, + attributeName); + if (index < 0) + { + return attributesCount; + } + + // Shift the other attributes in the array. + System.arraycopy(attributes, index + 1, + attributes, index, + attributesCount - index - 1); + + // Clear the last entry in the array. + attributes[--attributesCount] = null; + + return attributesCount; + } + + + /** + * Finds the index of the attribute with the given name in the given + * array of attributes. + */ + private int findAttribute(int attributesCount, + Attribute[] attributes, + String attributeName) + { + for (int index = 0; index < attributesCount; index++) + { + if (attributes[index].getAttributeName(targetClass).equals(attributeName)) + { + return index; + } + } + + return -1; + } +} diff --git a/src/proguard/classfile/editor/BridgeMethodFixer.java b/src/proguard/classfile/editor/BridgeMethodFixer.java new file mode 100644 index 000000000..2f1120d78 --- /dev/null +++ b/src/proguard/classfile/editor/BridgeMethodFixer.java @@ -0,0 +1,117 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 MemberVisitor fixes all inappropriate bridge access flags of the + * program methods that it visits, checking whether the methods to which they + * bridge have the same name. Some compilers, like in Eclipse and in later + * versions of JDK 1.6, complain if they can't find the method with the same + * name. + * + * @author Eric Lafortune + */ +public class BridgeMethodFixer +extends SimplifiedVisitor +implements MemberVisitor, + AttributeVisitor, + InstructionVisitor, + ConstantVisitor +{ + private static final boolean DEBUG = false; + + + // Return values for the visitor methods. + private String bridgedMethodName; + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if ((programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_BRIDGE) != 0) + { + programMethod.attributesAccept(programClass, this); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Go over the instructions of the bridge method. + codeAttribute.instructionsAccept(clazz, method, this); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + // Get the name of the bridged method. + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + + // Check if the name is different. + if (!method.getName(clazz).equals(bridgedMethodName)) + { + if (DEBUG) + { + System.out.println("BridgeMethodFixer: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] does not bridge to ["+bridgedMethodName+"]"); + } + + // Clear the bridge flag. + ((ProgramMethod)method).u2accessFlags &= ~ClassConstants.INTERNAL_ACC_BRIDGE; + } + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + bridgedMethodName = refConstant.getName(clazz); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/ClassEditor.java b/src/proguard/classfile/editor/ClassEditor.java new file mode 100644 index 000000000..7703c9dc5 --- /dev/null +++ b/src/proguard/classfile/editor/ClassEditor.java @@ -0,0 +1,255 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 interfaces and class members to a given class. + * Elements to be added must be filled out beforehand, including their + * references to the constant pool. + * + * @author Eric Lafortune + */ +public class ClassEditor +{ + private static final boolean DEBUG = false; + + private ProgramClass targetClass; + + + /** + * Creates a new ClassEditor that will edit elements in the given + * target class. + */ + public ClassEditor(ProgramClass targetClass) + { + this.targetClass = targetClass; + } + + + /** + * Adds the given interface. + */ + public void addInterface(int interfaceConstantIndex) + { + int interfacesCount = targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + // Make sure there is enough space for the new interface. + if (interfaces.length <= interfacesCount) + { + targetClass.u2interfaces = new int[interfacesCount+1]; + System.arraycopy(interfaces, 0, + targetClass.u2interfaces, 0, + interfacesCount); + interfaces = targetClass.u2interfaces; + } + + if (DEBUG) + { + System.out.println(targetClass.getName()+": adding interface ["+targetClass.getClassName(interfaceConstantIndex)+"]"); + } + + // Add the interface. + interfaces[targetClass.u2interfacesCount++] = interfaceConstantIndex; + } + + /** + * Removes the given interface. + */ + public void removeInterface(int interfaceConstantIndex) + { + int interfacesCount = targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + int interfaceIndex = findInterfaceIndex(interfaceConstantIndex); + + // Shift the interface entries. + System.arraycopy(interfaces, interfaceIndex+1, + interfaces, interfaceIndex, + interfacesCount - interfaceIndex - 1); + + // Clear the last entry. + interfaces[--targetClass.u2interfacesCount] = 0; + } + + + /** + * Finds the index of the given interface in the target class. + */ + + private int findInterfaceIndex(int interfaceConstantIndex) + { + int interfacesCount = targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + for (int index = 0; index < interfacesCount; index++) + { + if (interfaces[index] == interfaceConstantIndex) + { + return index; + } + } + + return interfacesCount; + } + + + /** + * Adds the given field. + */ + public void addField(Field field) + { + int fieldsCount = targetClass.u2fieldsCount; + Field[] fields = targetClass.fields; + + // Make sure there is enough space for the new field. + if (fields.length <= fieldsCount) + { + targetClass.fields = new ProgramField[fieldsCount+1]; + System.arraycopy(fields, 0, + targetClass.fields, 0, + fieldsCount); + fields = targetClass.fields; + } + + if (DEBUG) + { + System.out.println(targetClass.getName()+": adding field ["+field.getName(targetClass)+" "+field.getDescriptor(targetClass)+"]"); + } + + // Add the field. + fields[targetClass.u2fieldsCount++] = field; + } + + + /** + * Removes the given field. Note that removing a field that is still being + * referenced can cause unpredictable effects. + */ + public void removeField(Field field) + { + int fieldsCount = targetClass.u2fieldsCount; + Field[] fields = targetClass.fields; + + int fieldIndex = findFieldIndex(field); + + // Shift the field entries. + System.arraycopy(fields, fieldIndex+1, + fields, fieldIndex, + fieldsCount - fieldIndex - 1); + + // Clear the last entry. + fields[--targetClass.u2fieldsCount] = null; + } + + + /** + * Finds the index of the given field in the target class. + */ + + private int findFieldIndex(Field field) + { + int fieldsCount = targetClass.u2fieldsCount; + Field[] fields = targetClass.fields; + + for (int index = 0; index < fieldsCount; index++) + { + if (fields[index].equals(field)) + { + return index; + } + } + + return fieldsCount; + } + + + /** + * Adds the given method. + */ + public void addMethod(Method method) + { + int methodsCount = targetClass.u2methodsCount; + Method[] methods = targetClass.methods; + + // Make sure there is enough space for the new method. + if (methods.length <= methodsCount) + { + targetClass.methods = new ProgramMethod[methodsCount+1]; + System.arraycopy(methods, 0, + targetClass.methods, 0, + methodsCount); + methods = targetClass.methods; + } + + if (DEBUG) + { + System.out.println(targetClass.getName()+": adding method ["+method.getName(targetClass)+method.getDescriptor(targetClass)+"]"); + } + + // Add the method. + methods[targetClass.u2methodsCount++] = method; + } + + + /** + * Removes the given method. Note that removing a method that is still being + * referenced can cause unpredictable effects. + */ + public void removeMethod(Method method) + { + int methodsCount = targetClass.u2methodsCount; + Method[] methods = targetClass.methods; + + int methodIndex = findMethodIndex(method); + + // Shift the method entries. + System.arraycopy(methods, methodIndex+1, + methods, methodIndex, + methodsCount - methodIndex - 1); + + // Clear the last entry. + methods[--targetClass.u2methodsCount] = null; + } + + + /** + * Finds the index of the given method in the target class. + */ + + private int findMethodIndex(Method method) + { + int methodsCount = targetClass.u2methodsCount; + Method[] methods = targetClass.methods; + + for (int index = 0; index < methodsCount; index++) + { + if (methods[index].equals(method)) + { + return index; + } + } + + return methodsCount; + } +} diff --git a/src/proguard/classfile/editor/ClassElementSorter.java b/src/proguard/classfile/editor/ClassElementSorter.java new file mode 100644 index 000000000..9875a292b --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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/ClassMemberSorter.java b/src/proguard/classfile/editor/ClassMemberSorter.java new file mode 100644 index 000000000..ed0b5b14f --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 000000000..1f8b3966e --- /dev/null +++ b/src/proguard/classfile/editor/ClassReferenceFixer.java @@ -0,0 +1,546 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + + /** + * 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)) + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + // Update the descriptor. + programField.u2descriptorIndex = + constantPoolEditor.addUtf8Constant(newDescriptor); + + // Update the name, if requested. + if (ensureUniqueMemberNames) + { + String name = programField.getName(programClass); + String newName = newUniqueMemberName(name, descriptor); + programField.u2nameIndex = + constantPoolEditor.addUtf8Constant(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)) + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + // Update the descriptor. + programMethod.u2descriptorIndex = + constantPoolEditor.addUtf8Constant(newDescriptor); + + // Update the name, if requested. + if (ensureUniqueMemberNames) + { + String name = programMethod.getName(programClass); + String newName = newUniqueMemberName(name, descriptor); + programMethod.u2nameIndex = + constantPoolEditor.addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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.INTERNAL_INNER_CLASS_SEPARATOR); + if (index >= 0) + { + innerClassesInfo.u2innerNameIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(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(); + boolean isInnerClassName = descriptorClassEnumeration.isInnerClassName(); + String fluff = descriptorClassEnumeration.nextFluff(); + + String newClassName = newClassName(className, + referencedClasses[index++]); + + // Strip the outer class name again, if it's an inner class. + if (isInnerClassName) + { + newClassName = + newClassName.substring(newClassName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR)+1); + } + + newDescriptorBuffer.append(newClassName); + newDescriptorBuffer.append(fluff); + } + + return newDescriptorBuffer.toString(); + } + + + /** + * Returns a unique class member name, based on the given name and descriptor. + */ + private String newUniqueMemberName(String name, String descriptor) + { + 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/CodeAttributeComposer.java b/src/proguard/classfile/editor/CodeAttributeComposer.java new file mode 100644 index 000000000..c59b7123b --- /dev/null +++ b/src/proguard/classfile/editor/CodeAttributeComposer.java @@ -0,0 +1,918 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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; +import proguard.util.ArrayUtil; + +import java.util.Arrays; + +/** + * 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 = false; + //*/ + + + private static final int MAXIMUM_LEVELS = 32; + private static final int INVALID = -1; + + + private final boolean allowExternalExceptionHandlers; + private final boolean shrinkInstructions; + + 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 + 1]; + + 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(); + private final InstructionWriter instructionWriter = new InstructionWriter(); + + + /** + * Creates a new CodeAttributeComposer that doesn't allow external exception + * handlers and that automatically shrinks instructions. + */ + public CodeAttributeComposer() + { + this(false, true); + } + + + /** + * Creates a new CodeAttributeComposer. + * @param allowExternalExceptionHandlers specifies whether exception + * handlers can lie outside the code + * fragment in which exceptions are + * defined. + * @param shrinkInstructions specifies whether instructions + * should automatically be shrunk + * before being written. + */ + public CodeAttributeComposer(boolean allowExternalExceptionHandlers, + boolean shrinkInstructions) + { + this.allowExternalExceptionHandlers = allowExternalExceptionHandlers; + this.shrinkInstructions = shrinkInstructions; + } + + + /** + * Starts a new code definition. + */ + public void reset() + { + maximumCodeLength = 0; + codeLength = 0; + exceptionTableLength = 0; + level = -1; + + instructionWriter.reset(ClassConstants.TYPICAL_CODE_LENGTH); + } + + + /** + * 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 (more + * precisely, the maximum old instruction + * offset or label that is specified, plus + * one). + */ + 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. + // It's only a rough initial estimate for the code length, not even + // necessarily a length expressed in bytes. + maximumCodeLength += maximumCodeFragmentLength; + + ensureCodeLength(maximumCodeLength); + + // Try to reuse the previous array for this code fragment. + if (instructionOffsetMap[level].length <= maximumCodeFragmentLength) + { + instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1]; + } + + // Initialize the offset map. + for (int index = 0; index <= maximumCodeFragmentLength; index++) + { + instructionOffsetMap[level][index] = INVALID; + } + + // Remember the location of the code fragment. + codeFragmentOffsets[level] = codeLength; + codeFragmentLengths[level] = maximumCodeFragmentLength; + } + + + /** + * Appends the given instruction with the given old offset. + * Branch instructions must fit, for instance by enabling automatic + * shrinking of instructions. + * @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 (shrinkInstructions) + { + instruction = instruction.shrink(); + } + + if (DEBUG) + { + println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset)); + } + + // Make sure the code and offset arrays are large enough. + int newCodeLength = codeLength + instruction.length(codeLength); + + ensureCodeLength(newCodeLength); + + // Remember the old offset of the appended instruction. + oldInstructionOffsets[codeLength] = oldInstructionOffset; + + // Fill out the new offset of the appended instruction. + instructionOffsetMap[level][oldInstructionOffset] = codeLength; + + // Write the instruction. The instruction writer may widen it later on, + // if necessary. + instruction.accept(null, + null, + new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), + codeLength, + instructionWriter); + //instruction.write(code, codeLength); + + // Continue appending at the next instruction offset. + codeLength = newCodeLength; + } + + + /** + * 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)"); + } + + // Make sure the code and offset arrays are large enough. + ensureCodeLength(codeLength + 1); + + // Remember the old offset of the following instruction. + oldInstructionOffsets[codeLength] = oldInstructionOffset; + + // Fill out the new offset of the following instruction. + instructionOffsetMap[level][oldInstructionOffset] = codeLength; + } + + + /** + * Appends the given instruction without defined offsets. + * @param instructions the instructions to be appended. + */ + public void appendInstructions(Instruction[] instructions) + { + for (int index = 0; index < instructions.length; index++) + { + appendInstruction(instructions[index]); + } + } + + + /** + * Appends the given instruction without a defined offset. + * Branch instructions should have a label, to allow computing the + * new relative offset. + * Branch instructions must fit, for instance by enabling automatic + * shrinking of instructions. + * @param instruction the instruction to be appended. + */ + public void appendInstruction(Instruction instruction) + { + if (shrinkInstructions) + { + instruction = instruction.shrink(); + } + + if (DEBUG) + { + println("["+codeLength+"] <- ", instruction.toString()); + } + + // Make sure the code array is large enough. + int newCodeLength = codeLength + instruction.length(codeLength); + + ensureCodeLength(newCodeLength); + + // Write the instruction. The instruction writer may widen it later on, + // if necessary. + instruction.accept(null, + null, + new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), + codeLength, + instructionWriter); + //instruction.write(code, codeLength); + + // Continue appending at the next instruction offset. + codeLength = newCodeLength; + } + + + /** + * 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; + } + + // Add the exception. + exceptionTable = + (ExceptionInfo[])ArrayUtil.add(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. The instruction writer may still + // widen it later on, if necessary. + instruction.accept(null, + null, + new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), + instructionOffset, + instructionWriter); + //instruction.write(code, codeLength); + + // 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]; + + // Try to remap the exception handlers that couldn't be remapped before. + if (allowExternalExceptionHandlers) + { + for (int index = 0; index < exceptionTableLength; index++) + { + ExceptionInfo exceptionInfo = exceptionTable[index]; + + // Unmapped exception handlers are still negated. + int handlerPC = -exceptionInfo.u2handlerPC; + if (handlerPC > 0) + { + if (remappableExceptionHandler(handlerPC)) + { + exceptionInfo.u2handlerPC = newInstructionOffset(handlerPC); + } + else if (level == 0) + { + throw new IllegalStateException("Couldn't remap exception handler offset ["+handlerPC+"]"); + } + } + } + } + + 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 (done before). + //codeAttribute.exceptionsAccept(clazz, method, this); + + // Remove exceptions with empty code blocks (done before). + //codeAttribute.u2exceptionTableLength = + // removeEmptyExceptions(codeAttribute.exceptionTable, + // codeAttribute.u2exceptionTableLength); + + // Make sure instructions are widened if necessary. + instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); + + 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 = newBranchOffset(offset, + branchInstruction.branchOffset); + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + // Adjust the default jump offset. + switchInstruction.defaultOffset = newBranchOffset(offset, + switchInstruction.defaultOffset); + + // Adjust the jump offsets. + updateJumpOffsets(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 = newInstructionOffset(exceptionInfo.u2startPC); + exceptionInfo.u2endPC = newInstructionOffset(exceptionInfo.u2endPC); + + // See if we can remap the handler right away. Unmapped exception + // handlers are negated, in order to mark them as external. + int handlerPC = exceptionInfo.u2handlerPC; + exceptionInfo.u2handlerPC = + !allowExternalExceptionHandlers || + remappableExceptionHandler(handlerPC) ? + newInstructionOffset(handlerPC) : + -handlerPC; + } + + + // Implementations for StackMapFrameVisitor. + + public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) + { + // Remap the stack map frame offset. + int stackMapFrameOffset = newInstructionOffset(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 = newInstructionOffset(uninitializedType.u2newInstructionOffset); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + // Remap the code offset. + lineNumberInfo.u2startPC = newInstructionOffset(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 = newInstructionOffset(localVariableInfo.u2startPC); + int endPC = newInstructionOffset(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 = newInstructionOffset(localVariableTypeInfo.u2startPC); + int endPC = newInstructionOffset(localVariableTypeInfo.u2startPC + + localVariableTypeInfo.u2length); + + localVariableTypeInfo.u2startPC = startPC; + localVariableTypeInfo.u2length = endPC - startPC; + } + + + // Small utility methods. + + /** + * Make sure the code arrays have at least the given size. + */ + private void ensureCodeLength(int newCodeLength) + { + if (code.length < newCodeLength) + { + // Add 20% to avoid extending the arrays too often. + newCodeLength = newCodeLength * 6 / 5; + + code = ArrayUtil.extendArray(code, newCodeLength); + oldInstructionOffsets = ArrayUtil.extendArray(oldInstructionOffsets, newCodeLength); + + instructionWriter.extend(newCodeLength); + } + } + + + /** + * Adjusts the given jump offsets for the instruction at the given offset. + */ + private void updateJumpOffsets(int offset, int[] jumpOffsets) + { + for (int index = 0; index < jumpOffsets.length; index++) + { + jumpOffsets[index] = newBranchOffset(offset, jumpOffsets[index]); + } + } + + + /** + * Computes the new branch offset for the instruction at the given new offset + * with the given old branch offset. + */ + private int newBranchOffset(int newInstructionOffset, int oldBranchOffset) + { + if (newInstructionOffset < 0 || + newInstructionOffset > codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+newInstructionOffset +"] in code with length ["+codeLength+"]"); + } + + int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset]; + + return newInstructionOffset(oldInstructionOffset + oldBranchOffset) - + newInstructionOffset(oldInstructionOffset); + } + + + /** + * Computes the new instruction offset for the instruction at the given old + * offset. + */ + private int newInstructionOffset(int oldInstructionOffset) + { + if (oldInstructionOffset < 0 || + oldInstructionOffset > codeFragmentLengths[level]) + { + throw new IllegalArgumentException("Instruction offset ["+oldInstructionOffset +"] out of range in code fragment with length ["+codeFragmentLengths[level]+"] at level "+level); + } + + int newInstructionOffset = instructionOffsetMap[level][oldInstructionOffset]; + if (newInstructionOffset == INVALID) + { + throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset +"] in code fragment at level "+level); + } + + return newInstructionOffset; + } + + + /** + * Returns whether the given old exception handler can be remapped in the + * current code fragment. + */ + private boolean remappableExceptionHandler(int oldInstructionOffset) + { + // Can we index in the array? + if (oldInstructionOffset > codeFragmentLengths[level]) + { + return false; + } + + // Do we have a valid new instruction offset, but not yet right after + // the code? That offset is only labeled for mapping try blocks, not + // for mapping handlers. + int newInstructionOffset = + instructionOffsetMap[level][oldInstructionOffset]; + + return newInstructionOffset > INVALID && + newInstructionOffset < codeLength; + } + + + /** + * 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. + Arrays.fill(exceptionInfos, newIndex, exceptionInfoCount, 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. + Arrays.fill(lineNumberInfos, newIndex, lineNumberInfoCount, 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. + Arrays.fill(localVariableInfos, newIndex, localVariableInfoCount, 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. + Arrays.fill(localVariableTypeInfos, newIndex, localVariableTypeInfoCount, 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(4); + composer.appendInstruction(0, new SimpleInstruction(InstructionConstants.OP_ICONST_0)); + composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ISTORE, 0)); + composer.appendInstruction(2, new BranchInstruction(InstructionConstants.OP_GOTO, 1)); + + composer.beginCodeFragment(4); + composer.appendInstruction(0, new VariableInstruction(InstructionConstants.OP_IINC, 0, 1)); + composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ILOAD, 0)); + composer.appendInstruction(2, new SimpleInstruction(InstructionConstants.OP_ICONST_5)); + 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/CodeAttributeEditor.java b/src/proguard/classfile/editor/CodeAttributeEditor.java new file mode 100644 index 000000000..337e0d4ef --- /dev/null +++ b/src/proguard/classfile/editor/CodeAttributeEditor.java @@ -0,0 +1,1199 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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; +import proguard.util.ArrayUtil; + +import java.util.Arrays; + +/** + * 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 CodeAttributeEditor +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ExceptionInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LineNumberInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + public static boolean DEBUG = false; + //*/ + + + private final boolean updateFrameSizes; + private final boolean shrinkInstructions; + + 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*/public boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + + private int[] newInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; + private int newOffset; + private boolean lengthIncreased; + + private int expectedStackMapFrameOffset; + + private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); + private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); + private final InstructionWriter instructionWriter = new InstructionWriter(); + + + /** + * Creates a new CodeAttributeEditor that automatically updates frame + * sizes and shrinks instructions. + */ + public CodeAttributeEditor() + { + this(true, true); + } + + + /** + * Creates a new CodeAttributeEditor. + * @param updateFrameSizes specifies whether frame sizes of edited code + * should be updated. + * @param shrinkInstructions specifies whether added instructions should + * automatically be shrunk before being written. + */ + public CodeAttributeEditor(boolean updateFrameSizes, + boolean shrinkInstructions) + { + this.updateFrameSizes = updateFrameSizes; + this.shrinkInstructions = shrinkInstructions; + } + + + /** + * Resets the accumulated code changes. + * @param codeLength the length of the code that will be edited next. + */ + public void reset(int codeLength) + { + // Try to reuse the previous arrays. + if (preInsertions.length < codeLength) + { + preInsertions = new Instruction[codeLength]; + replacements = new Instruction[codeLength]; + postInsertions = new Instruction[codeLength]; + deleted = new boolean[codeLength]; + } + else + { + Arrays.fill(preInsertions, 0, codeLength, null); + Arrays.fill(replacements, 0, codeLength, null); + Arrays.fill(postInsertions, 0, codeLength, null); + Arrays.fill(deleted, 0, codeLength, false); + } + + this.codeLength = codeLength; + + modified = false; + simple = true; + } + + + /** + * Extends the size of the accumulated code changes. + * @param codeLength the length of the code that will be edited next. + */ + public void extend(int codeLength) + { + // Try to reuse the previous arrays. + if (preInsertions.length < codeLength) + { + preInsertions = (Instruction[])ArrayUtil.extendArray(preInsertions, codeLength); + replacements = (Instruction[])ArrayUtil.extendArray(replacements, codeLength); + postInsertions = (Instruction[])ArrayUtil.extendArray(postInsertions, codeLength); + deleted = ArrayUtil.extendArray(deleted, codeLength); + } + else + { + Arrays.fill(preInsertions, this.codeLength, codeLength, null); + Arrays.fill(replacements, this.codeLength, codeLength, null); + Arrays.fill(postInsertions, this.codeLength, codeLength, null); + Arrays.fill(deleted, this.codeLength, codeLength, false); + } + + this.codeLength = codeLength; + } + + + /** + * Remembers to place the given instruction right before the instruction + * at the given offset. + * @param instructionOffset the offset of the instruction. + * @param instruction the new instruction. + */ + public void insertBeforeInstruction(int instructionOffset, Instruction instruction) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + preInsertions[instructionOffset] = shrinkInstructions ? + instruction.shrink() : + instruction; + + modified = true; + simple = false; + + } + + + /** + * Remembers to place the given instructions right before the instruction + * at the given offset. + * @param instructionOffset the offset of the instruction. + * @param instructions the new instructions. + */ + public void insertBeforeInstruction(int instructionOffset, Instruction[] instructions) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + CompositeInstruction instruction = + new CompositeInstruction(instructions); + + preInsertions[instructionOffset] = shrinkInstructions ? + instruction.shrink() : + instruction; + + modified = true; + simple = false; + + } + + + /** + * Remembers to replace the instruction at the given offset by the given + * instruction. + * @param instructionOffset the offset of the instruction to be replaced. + * @param instruction the new instruction. + */ + public void replaceInstruction(int instructionOffset, Instruction instruction) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + replacements[instructionOffset] = shrinkInstructions ? + instruction.shrink() : + instruction; + + modified = true; + } + + + /** + * Remembers to replace the instruction at the given offset by the given + * instructions. + * @param instructionOffset the offset of the instruction to be replaced. + * @param instructions the new instructions. + */ + public void replaceInstruction(int instructionOffset, Instruction[] instructions) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + CompositeInstruction instruction = + new CompositeInstruction(instructions); + + replacements[instructionOffset] = shrinkInstructions ? + instruction.shrink() : + instruction; + + modified = true; + } + + + /** + * Remembers to place the given instruction right after the instruction + * at the given offset. + * @param instructionOffset the offset of the instruction. + * @param instruction the new instruction. + */ + public void insertAfterInstruction(int instructionOffset, Instruction instruction) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + postInsertions[instructionOffset] = shrinkInstructions ? + instruction.shrink() : + instruction; + + modified = true; + simple = false; + } + + + /** + * Remembers to place the given instructions right after the instruction + * at the given offset. + * @param instructionOffset the offset of the instruction. + * @param instructions the new instructions. + */ + public void insertAfterInstruction(int instructionOffset, Instruction[] instructions) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + CompositeInstruction instruction = + new CompositeInstruction(instructions); + + postInsertions[instructionOffset] = shrinkInstructions ? + instruction.shrink() : + instruction; + + modified = true; + simple = false; + } + + + /** + * Remembers to delete the instruction at the given offset. + * @param instructionOffset the offset of the instruction to be deleted. + */ + public void deleteInstruction(int instructionOffset) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + deleted[instructionOffset] = true; + + modified = true; + simple = false; + } + + + /** + * Remembers not to delete the instruction at the given offset. + * @param instructionOffset the offset of the instruction not to be deleted. + */ + public void undeleteInstruction(int instructionOffset) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + deleted[instructionOffset] = false; + } + + + /** + * Clears all modifications of the instruction at the given offset. + * @param instructionOffset the offset of the instruction to be deleted. + */ + public void clearModifications(int instructionOffset) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + preInsertions[instructionOffset] = null; + replacements[instructionOffset] = null; + postInsertions[instructionOffset] = null; + deleted[instructionOffset] = false; + } + + + /** + * Returns whether the code has been modified in any way. + */ + public boolean isModified() + { + return modified; + } + + + /** + * Returns whether the instruction at the given offset has been modified + * in any way. + */ + public boolean isModified(int instructionOffset) + { + return preInsertions[instructionOffset] != null || + replacements[instructionOffset] != null || + postInsertions[instructionOffset] != null || + deleted[instructionOffset]; + } + + + // 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 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()+")"); + + throw ex; + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Do we have to update the code? + if (modified) + { + if (DEBUG) + { + System.out.println("CodeAttributeEditor: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + } + + // Can we perform a faster simple replacement of instructions? + if (canPerformSimpleReplacements(codeAttribute)) + { + if (DEBUG) + { + System.out.println(" Simple editing"); + } + + // Simply overwrite the instructions. + performSimpleReplacements(codeAttribute); + } + else + { + if (DEBUG) + { + System.out.println(" Full editing"); + } + + // Move and remap the instructions. + codeAttribute.u4codeLength = + updateInstructions(clazz, method, codeAttribute); + + // Update the exception table. + codeAttribute.exceptionsAccept(clazz, method, this); + + // Remove exceptions with empty code blocks. + codeAttribute.u2exceptionTableLength = + removeEmptyExceptions(codeAttribute.exceptionTable, + codeAttribute.u2exceptionTableLength); + + // Update the line number table and the local variable tables. + codeAttribute.attributesAccept(clazz, method, this); + } + + // Make sure instructions are widened if necessary. + instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); + } + + // Update the maximum stack size and local variable frame size. + if (updateFrameSizes) + { + stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + } + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + // Update all stack map entries. + expectedStackMapFrameOffset = -1; + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + // Update all stack map table entries. + expectedStackMapFrameOffset = 0; + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + // Update 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) + { + // Update all local variable table entries. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Update all local variable table entries. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + /** + * Checks if it is possible to modifies the given code without having to + * update any offsets. + * @param codeAttribute the code to be changed. + * @return the new code length. + */ + private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute) + { + if (!simple) + { + return false; + } + + byte[] code = codeAttribute.code; + int codeLength = codeAttribute.u4codeLength; + + // Go over all replacement instructions. + for (int offset = 0; offset < codeLength; offset++) + { + // Check if the replacement instruction, if any, has a different + // length than the original instruction. + Instruction replacementInstruction = replacements[offset]; + if (replacementInstruction != null && + replacementInstruction.length(offset) != + InstructionFactory.create(code, offset).length(offset)) + { + return false; + } + } + + return true; + } + + + /** + * Modifies the given code without updating any offsets. + * @param codeAttribute the code to be changed. + */ + private void performSimpleReplacements(CodeAttribute codeAttribute) + { + int codeLength = codeAttribute.u4codeLength; + + // Go over all replacement instructions. + for (int offset = 0; offset < codeLength; offset++) + { + // Overwrite the original instruction with the replacement + // instruction if any. + Instruction replacementInstruction = replacements[offset]; + if (replacementInstruction != null) + { + replacementInstruction.write(codeAttribute, offset); + + if (DEBUG) + { + System.out.println(" Replaced "+replacementInstruction.toString(offset)); + } + } + } + } + + + /** + * Modifies the given code based on the previously specified changes. + * @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(Clazz clazz, + Method method, + CodeAttribute codeAttribute) + { + byte[] oldCode = codeAttribute.code; + int oldLength = codeAttribute.u4codeLength; + + // Make sure there is a sufficiently large instruction offset map. + if (newInstructionOffsets.length < oldLength + 1) + { + newInstructionOffsets = new int[oldLength + 1]; + } + + // Fill out the instruction offset map. + int newLength = mapInstructions(oldCode, + oldLength); + + // Create a new code array if necessary. + if (lengthIncreased) + { + codeAttribute.code = new byte[newLength]; + } + + // Prepare for possible widening of instructions. + instructionWriter.reset(newLength); + + // Move the instructions into the new code array. + moveInstructions(clazz, + method, + codeAttribute, + oldCode, + oldLength); + + // We can return the new length. + return newLength; + } + + + /** + * Fills out the instruction offset map for the given code block. + * @param oldCode the instructions to be moved. + * @param oldLength the code length. + * @return the new code length. + */ + private int mapInstructions(byte[] oldCode, int oldLength) + { + // Start mapping instructions at the beginning. + newOffset = 0; + lengthIncreased = false; + + int oldOffset = 0; + do + { + // Get the next instruction. + Instruction instruction = InstructionFactory.create(oldCode, oldOffset); + + // Compute the mapping of the instruction. + mapInstruction(oldOffset, instruction); + + oldOffset += instruction.length(oldOffset); + + if (newOffset > oldOffset) + { + lengthIncreased = true; + } + } + while (oldOffset < oldLength); + + // Also add an entry for the first offset after the code. + newInstructionOffsets[oldOffset] = newOffset; + + return newOffset; + } + + + /** + * Fills out the instruction offset map for the given instruction. + * @param oldOffset the instruction's old offset. + * @param instruction the instruction to be moved. + */ + private void mapInstruction(int oldOffset, + Instruction instruction) + { + newInstructionOffsets[oldOffset] = newOffset; + + // Account for the pre-inserted instruction, if any. + Instruction preInstruction = preInsertions[oldOffset]; + if (preInstruction != null) + { + newOffset += preInstruction.length(newOffset); + } + + // Account for the replacement instruction, or for the current + // instruction, if it shouldn't be deleted. + Instruction replacementInstruction = replacements[oldOffset]; + if (replacementInstruction != null) + { + newOffset += replacementInstruction.length(newOffset); + } + else if (!deleted[oldOffset]) + { + // Note that the instruction's length may change at its new offset, + // e.g. if it is a switch instruction. + newOffset += instruction.length(newOffset); + } + + // Account for the post-inserted instruction, if any. + Instruction postInstruction = postInsertions[oldOffset]; + if (postInstruction != null) + { + newOffset += postInstruction.length(newOffset); + } + } + + + /** + * Moves the given code block to the new offsets. + * @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(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + byte[] oldCode, + int oldLength) + { + // Start writing instructions at the beginning. + newOffset = 0; + + int oldOffset = 0; + do + { + // Get the next instruction. + Instruction instruction = InstructionFactory.create(oldCode, oldOffset); + + // Move the instruction to its new offset. + moveInstruction(clazz, + method, + codeAttribute, + oldOffset, + instruction); + + oldOffset += instruction.length(oldOffset); + } + while (oldOffset < oldLength); + } + + + /** + * Moves the given instruction to its new offset. + * @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(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int oldOffset, + Instruction instruction) + { + // Update and insert the pre-inserted instruction, if any. + Instruction preInstruction = preInsertions[oldOffset]; + if (preInstruction != null) + { + if (DEBUG) + { + System.out.println(" Pre-inserted ["+oldOffset+"] -> "+preInstruction.toString(newOffset)); + } + + // Update the instruction. + preInstruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + + // Update and insert the replacement instruction, or the current + // instruction, if it shouldn't be deleted. + Instruction replacementInstruction = replacements[oldOffset]; + if (replacementInstruction != null) + { + if (DEBUG) + { + System.out.println(" Replaced ["+oldOffset+"] -> "+replacementInstruction.toString(newOffset)); + } + + // Update the instruction. + replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + else if (!deleted[oldOffset]) + { + if (DEBUG) + { + System.out.println(" Copied ["+oldOffset+"] -> "+instruction.toString(newOffset)); + } + + // Update the instruction. + instruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + + // Update and insert the post-inserted instruction, if any. + Instruction postInstruction = postInsertions[oldOffset]; + if (postInstruction != null) + { + if (DEBUG) + { + System.out.println(" Post-inserted ["+oldOffset+"] -> "+postInstruction.toString(newOffset)); + } + + // Update the instruction. + postInstruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + // Write out the instruction. + instructionWriter.visitSimpleInstruction(clazz, + method, + codeAttribute, + newOffset, + simpleInstruction); + + newOffset += simpleInstruction.length(newOffset); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Write out the instruction. + instructionWriter.visitConstantInstruction(clazz, + method, + codeAttribute, + newOffset, + constantInstruction); + + newOffset += constantInstruction.length(newOffset); + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + // Write out the instruction. + instructionWriter.visitVariableInstruction(clazz, + method, + codeAttribute, + newOffset, + variableInstruction); + + newOffset += variableInstruction.length(newOffset); + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + // Adjust the branch offset. + branchInstruction.branchOffset = newBranchOffset(offset, + branchInstruction.branchOffset); + + // Write out the instruction. + instructionWriter.visitBranchInstruction(clazz, + method, + codeAttribute, + newOffset, + branchInstruction); + + newOffset += branchInstruction.length(newOffset); + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + // Adjust the default jump offset. + tableSwitchInstruction.defaultOffset = newBranchOffset(offset, + tableSwitchInstruction.defaultOffset); + + // Adjust the jump offsets. + newJumpOffsets(offset, + tableSwitchInstruction.jumpOffsets); + + // Write out the instruction. + instructionWriter.visitTableSwitchInstruction(clazz, + method, + codeAttribute, + newOffset, + tableSwitchInstruction); + + newOffset += tableSwitchInstruction.length(newOffset); + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + // Adjust the default jump offset. + lookUpSwitchInstruction.defaultOffset = newBranchOffset(offset, + lookUpSwitchInstruction.defaultOffset); + + // Adjust the jump offsets. + newJumpOffsets(offset, + lookUpSwitchInstruction.jumpOffsets); + + // Write out the instruction. + instructionWriter.visitLookUpSwitchInstruction(clazz, + method, + codeAttribute, + newOffset, + lookUpSwitchInstruction); + + newOffset += lookUpSwitchInstruction.length(newOffset); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + // Update the code offsets. Note that the instruction offset map also has + // an entry for the first offset after the code, for u2endPC. + exceptionInfo.u2startPC = newInstructionOffset(exceptionInfo.u2startPC); + exceptionInfo.u2endPC = newInstructionOffset(exceptionInfo.u2endPC); + exceptionInfo.u2handlerPC = newInstructionOffset(exceptionInfo.u2handlerPC); + } + + + // Implementations for StackMapFrameVisitor. + + public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) + { + // Update the stack map frame offset. + int stackMapFrameOffset = newInstructionOffset(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) + { + // Update the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); + + // Update the verification type offset. + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + // Update the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); + + // Update the verification type offsets. + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + // Update the stack map frame offset. + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); + + // Update 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) + { + // Update the offset of the 'new' instruction. + uninitializedType.u2newInstructionOffset = newInstructionOffset(uninitializedType.u2newInstructionOffset); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + // Update the code offset. + lineNumberInfo.u2startPC = newInstructionOffset(lineNumberInfo.u2startPC); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Update the code offset and length. + int newStartPC = newInstructionOffset(localVariableInfo.u2startPC); + int newEndPC = newInstructionOffset(localVariableInfo.u2startPC + + localVariableInfo.u2length); + + localVariableInfo.u2length = newEndPC - newStartPC; + localVariableInfo.u2startPC = newStartPC; + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Update the code offset and length. + int newStartPC = newInstructionOffset(localVariableTypeInfo.u2startPC); + int newEndPC = newInstructionOffset(localVariableTypeInfo.u2startPC + + localVariableTypeInfo.u2length); + + localVariableTypeInfo.u2length = newEndPC - newStartPC; + localVariableTypeInfo.u2startPC = newStartPC; + } + + + // Small utility methods. + + /** + * Adjusts the given jump offsets for the instruction at the given offset. + */ + private void newJumpOffsets(int oldInstructionOffset, int[] oldJumpOffsets) + { + for (int index = 0; index < oldJumpOffsets.length; index++) + { + oldJumpOffsets[index] = newBranchOffset(oldInstructionOffset, oldJumpOffsets[index]); + } + } + + + /** + * Computes the new branch offset for the instruction at the given offset + * with the given branch offset. + */ + private int newBranchOffset(int oldInstructionOffset, int oldBranchOffset) + { + return newInstructionOffset(oldInstructionOffset + oldBranchOffset) - newOffset; + } + + + /** + * Computes the new instruction offset for the instruction at the given offset. + */ + private int newInstructionOffset(int oldInstructionOffset) + { + if (oldInstructionOffset < 0 || + oldInstructionOffset > codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset+"] in code with length ["+codeLength+"]"); + } + + return newInstructionOffsets[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; + } + } + + 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; + } + } + + return newIndex; + } + + + /** + * This instruction is a composite of other instructions, for local use + * inside the editor class only. + */ + private class CompositeInstruction + extends Instruction + { + private Instruction[] instructions; + + + private CompositeInstruction(Instruction[] instructions) + { + this.instructions = instructions; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + for (int index = 0; index < instructions.length; index++) + { + instructions[index] = instructions[index].shrink(); + } + + return this; + } + + + public void write(byte[] code, int offset) + { + for (int index = 0; index < instructions.length; index++) + { + Instruction instruction = instructions[index]; + + instruction.write(code, offset); + + offset += instruction.length(offset); + } + } + + + protected void readInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't read composite instruction"); + } + + + protected void writeInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't write composite instruction"); + } + + + public int length(int offset) + { + int newOffset = offset; + + for (int index = 0; index < instructions.length; index++) + { + newOffset += instructions[index].length(newOffset); + } + + return newOffset - offset; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + if (instructionVisitor != CodeAttributeEditor.this) + { + throw new UnsupportedOperationException("Unexpected visitor ["+instructionVisitor+"]"); + } + + for (int index = 0; index < instructions.length; index++) + { + Instruction instruction = instructions[index]; + + instruction.accept(clazz, method, codeAttribute, offset, CodeAttributeEditor.this); + + offset += instruction.length(offset); + } + } + + + // Implementations for Object. + + public String toString() + { + StringBuffer stringBuffer = new StringBuffer(); + + for (int index = 0; index < instructions.length; index++) + { + stringBuffer.append(instructions[index].toString()).append("; "); + } + + return stringBuffer.toString(); + } + } +} diff --git a/src/proguard/classfile/editor/CodeAttributeEditorResetter.java b/src/proguard/classfile/editor/CodeAttributeEditorResetter.java new file mode 100644 index 000000000..8f767c7b3 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..476edd69c --- /dev/null +++ b/src/proguard/classfile/editor/ComparableConstant.java @@ -0,0 +1,249 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 Comparable wrapper of Constant + * 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 ComparableConstant +extends SimplifiedVisitor +implements Comparable, ConstantVisitor +{ + private static final int[] PRIORITIES = new int[19]; + static + { + PRIORITIES[ClassConstants.CONSTANT_Integer] = 0; // Possibly byte index (ldc). + PRIORITIES[ClassConstants.CONSTANT_Float] = 1; + PRIORITIES[ClassConstants.CONSTANT_String] = 2; + PRIORITIES[ClassConstants.CONSTANT_Class] = 3; + PRIORITIES[ClassConstants.CONSTANT_Long] = 4; // Always wide index (ldc2_w). + PRIORITIES[ClassConstants.CONSTANT_Double] = 5; // Always wide index (ldc2_w). + PRIORITIES[ClassConstants.CONSTANT_Fieldref] = 6; // Always wide index (getfield,...). + PRIORITIES[ClassConstants.CONSTANT_Methodref] = 7; // Always wide index (invokespecial,...). + PRIORITIES[ClassConstants.CONSTANT_InterfaceMethodref] = 8; // Always wide index (invokeinterface). + PRIORITIES[ClassConstants.CONSTANT_InvokeDynamic] = 9; // Always wide index (invokedynamic). + PRIORITIES[ClassConstants.CONSTANT_MethodHandle] = 10; + PRIORITIES[ClassConstants.CONSTANT_NameAndType] = 11; + PRIORITIES[ClassConstants.CONSTANT_MethodType] = 12; + PRIORITIES[ClassConstants.CONSTANT_Utf8] = 13; + } + + 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) + { + int value = integerConstant.getValue(); + int otherValue = ((IntegerConstant)otherConstant).getValue(); + result = value < otherValue ? -1 : + value == otherValue ? 0 : + 1; + } + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + long value = longConstant.getValue(); + long otherValue = ((LongConstant)otherConstant).getValue(); + result = value < otherValue ? -1 : + value == otherValue ? 0 : + 1; + } + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + result = Float.compare(floatConstant.getValue(), + ((FloatConstant)otherConstant).getValue()); + } + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + result = Double.compare(doubleConstant.getValue(), + ((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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + InvokeDynamicConstant otherInvokeDynamicConstant = (InvokeDynamicConstant)otherConstant; + + int index = invokeDynamicConstant.getBootstrapMethodAttributeIndex(); + int otherIndex = otherInvokeDynamicConstant.getBootstrapMethodAttributeIndex(); + + result = index < otherIndex ? -1 : + index > otherIndex ? 1 : + (invokeDynamicConstant.getName(clazz) + ' ' + + invokeDynamicConstant.getType(clazz)) + .compareTo + (otherInvokeDynamicConstant.getName(clazz) + ' ' + + otherInvokeDynamicConstant.getType(clazz)); + } + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + MethodHandleConstant otherMethodHandleConstant = (MethodHandleConstant)otherConstant; + + int kind = methodHandleConstant.getReferenceKind(); + int otherKind = methodHandleConstant.getReferenceKind(); + + result = kind < otherKind ? -1 : + kind > otherKind ? 1 : + (methodHandleConstant.getName(clazz) + ' ' + + methodHandleConstant.getType(clazz)) + .compareTo + (otherMethodHandleConstant.getName(clazz) + ' ' + + otherMethodHandleConstant.getType(clazz)); + } + + 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 visitMethodTypeConstant(Clazz clazz, MethodTypeConstant MethodTypeConstant) + { + MethodTypeConstant otherMethodTypeConstant = (MethodTypeConstant)otherConstant; + result = MethodTypeConstant.getType(clazz) + .compareTo + (otherMethodTypeConstant.getType(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)); + } + + + // Implementations for Object. + + public boolean equals(Object other) + { + return other != null && + this.getClass().equals(other.getClass()) && + this.getConstant().getClass().equals(((ComparableConstant)other).getConstant().getClass()) && + this.compareTo(other) == 0; + } + + + public int hashCode() + { + return this.getClass().hashCode(); + } +} diff --git a/src/proguard/classfile/editor/ConstantAdder.java b/src/proguard/classfile/editor/ConstantAdder.java new file mode 100644 index 000000000..9d20199d3 --- /dev/null +++ b/src/proguard/classfile/editor/ConstantAdder.java @@ -0,0 +1,239 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.ListUtil; + +/** + * 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 final ConstantPoolEditor constantPoolEditor; + + private int constantIndex; + + + /** + * Creates a new ConstantAdder that will copy constants into the given + * target class. + */ + public ConstantAdder(ProgramClass targetClass) + { + constantPoolEditor = new ConstantPoolEditor(targetClass); + } + + + /** + * Adds a copy of the specified constant in the given class and returns + * its index. If the specified index is 0, the returned value is 0 too. + */ + public int addConstant(Clazz clazz, int constantIndex) + { + clazz.constantPoolEntryAccept(constantIndex, this); + + return this.constantIndex; + } + + + /** + * Adds a copy of the given constant in the given class and returns + * its index. + */ + public int addConstant(Clazz clazz, Constant constant) + { + constant.accept(clazz, this); + + return this.constantIndex; + } + + + /** + * 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(integerConstant.getValue()); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + constantIndex = + constantPoolEditor.addLongConstant(longConstant.getValue()); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + constantIndex = + constantPoolEditor.addFloatConstant(floatConstant.getValue()); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + constantIndex = + constantPoolEditor.addDoubleConstant(doubleConstant.getValue()); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + constantIndex = + constantPoolEditor.addStringConstant(stringConstant.getString(clazz), + stringConstant.referencedClass, + stringConstant.referencedMember); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + constantIndex = + constantPoolEditor.addUtf8Constant(utf8Constant.getString()); + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // First add the name and type constant. + clazz.constantPoolEntryAccept(invokeDynamicConstant.u2nameAndTypeIndex, this); + + // Copy the referenced classes. + Clazz[] referencedClasses = invokeDynamicConstant.referencedClasses; + Clazz[] referencedClassesCopy = null; + if (referencedClasses != null) + { + referencedClassesCopy = new Clazz[referencedClasses.length]; + System.arraycopy(referencedClasses, 0, + referencedClassesCopy, 0, + referencedClasses.length); + } + + // Then add the actual invoke dynamic constant. + constantIndex = + constantPoolEditor.addInvokeDynamicConstant(invokeDynamicConstant.getBootstrapMethodAttributeIndex(), + constantIndex, + referencedClassesCopy); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + // First add the field ref, interface method ref, or method ref + // constant. + clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, this); + + // Then add the actual method handle constant. + constantIndex = + constantPoolEditor.addMethodHandleConstant(methodHandleConstant.getReferenceKind(), + constantIndex); + } + + + 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(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(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(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(classConstant.getName(clazz), + classConstant.referencedClass); + } + + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + constantIndex = + constantPoolEditor.addMethodTypeConstant(methodTypeConstant.getType(clazz)); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + constantIndex = + constantPoolEditor.addNameAndTypeConstant(nameAndTypeConstant.getName(clazz), + nameAndTypeConstant.getType(clazz)); + } +} diff --git a/src/proguard/classfile/editor/ConstantPoolEditor.java b/src/proguard/classfile/editor/ConstantPoolEditor.java new file mode 100644 index 000000000..7adbc444c --- /dev/null +++ b/src/proguard/classfile/editor/ConstantPoolEditor.java @@ -0,0 +1,782 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +/** + * This class can add constant pool entries to a given class. + * + * @author Eric Lafortune + */ +public class ConstantPoolEditor +{ + private static final boolean DEBUG = false; + + private ProgramClass targetClass; + + + /** + * Creates a new ConstantPoolEditor that will edit constants in the given + * target class. + */ + public ConstantPoolEditor(ProgramClass targetClass) + { + this.targetClass = targetClass; + } + + + /** + * Finds or creates a IntegerConstant constant pool entry with the given + * value. + * @return the constant pool index of the Utf8Constant. + */ + public int addIntegerConstant(int value) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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(new IntegerConstant(value)); + } + + + /** + * Finds or creates a LongConstant constant pool entry with the given value. + * @return the constant pool index of the LongConstant. + */ + public int addLongConstant(long value) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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(new LongConstant(value)); + } + + + /** + * Finds or creates a FloatConstant constant pool entry with the given + * value. + * @return the constant pool index of the FloatConstant. + */ + public int addFloatConstant(float value) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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(new FloatConstant(value)); + } + + + /** + * Finds or creates a DoubleConstant constant pool entry with the given + * value. + * @return the constant pool index of the DoubleConstant. + */ + public int addDoubleConstant(double value) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_Double) + { + DoubleConstant doubleConstant = (DoubleConstant)constant; + if (doubleConstant.getValue() == value) + { + return index; + } + } + } + + return addConstant(new DoubleConstant(value)); + } + + + /** + * Finds or creates a StringConstant constant pool entry with the given + * value. + * @return the constant pool index of the StringConstant. + */ + public int addStringConstant(String string, + Clazz referencedClass, + Member referencedMember) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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 stringConstant = (StringConstant)constant; + if (stringConstant.getString(targetClass).equals(string)) + { + return index; + } + } + } + + return addConstant(new StringConstant(addUtf8Constant(string), + referencedClass, + referencedMember)); + } + + + /** + * Finds or creates a InvokeDynamicConstant constant pool entry with the + * given bootstrap method constant pool entry index, method name, and + * descriptor. + * @return the constant pool index of the InvokeDynamicConstant. + */ + public int addInvokeDynamicConstant(int bootstrapMethodIndex, + String name, + String descriptor, + Clazz[] referencedClasses) + { + return addInvokeDynamicConstant(bootstrapMethodIndex, + addNameAndTypeConstant(name, descriptor), + referencedClasses); + } + + + /** + * Finds or creates a InvokeDynamicConstant constant pool entry with the given + * class constant pool entry index and name and type constant pool entry + * index. + * @return the constant pool index of the InvokeDynamicConstant. + */ + public int addInvokeDynamicConstant(int bootstrapMethodIndex, + int nameAndTypeIndex, + Clazz[] referencedClasses) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_InvokeDynamic) + { + InvokeDynamicConstant invokeDynamicConstant = (InvokeDynamicConstant)constant; + if (invokeDynamicConstant.u2bootstrapMethodAttributeIndex == bootstrapMethodIndex && + invokeDynamicConstant.u2nameAndTypeIndex == nameAndTypeIndex) + { + return index; + } + } + } + + return addConstant(new InvokeDynamicConstant(bootstrapMethodIndex, + nameAndTypeIndex, + referencedClasses)); + } + + + /** + * Finds or creates a MethodHandleConstant constant pool entry of the + * specified kind and with the given field ref, interface method ref, + * or method ref constant pool entry index. + * @return the constant pool index of the MethodHandleConstant. + */ + public int addMethodHandleConstant(int referenceKind, + int referenceIndex) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_MethodHandle) + { + MethodHandleConstant methodHandleConstant = (MethodHandleConstant)constant; + if (methodHandleConstant.u1referenceKind == referenceKind && + methodHandleConstant.u2referenceIndex == referenceIndex) + { + return index; + } + } + } + + return addConstant(new MethodHandleConstant(referenceKind, + referenceIndex)); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry for the given + * class and field. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(Clazz referencedClass, + Member referencedMember) + { + return addFieldrefConstant(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry with the given + * class name, field name, and descriptor. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addFieldrefConstant(className, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry with the given + * class name, field name, and descriptor. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(String className, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + return addFieldrefConstant(addClassConstant(className, referencedClass), + nameAndTypeIndex, + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry with the given + * class constant pool entry index, field name, and descriptor. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(int classIndex, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addFieldrefConstant(classIndex, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a FieldrefConstant constant pool entry with the given + * class constant pool entry index and name and type constant pool entry + * index. + * @return the constant pool index of the FieldrefConstant. + */ + public int addFieldrefConstant(int classIndex, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_Fieldref) + { + FieldrefConstant fieldrefConstant = (FieldrefConstant)constant; + if (fieldrefConstant.u2classIndex == classIndex && + fieldrefConstant.u2nameAndTypeIndex == nameAndTypeIndex) + { + return index; + } + } + } + + return addConstant(new FieldrefConstant(classIndex, + nameAndTypeIndex, + referencedClass, + referencedMember)); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry with the + * given class name, method name, and descriptor. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addInterfaceMethodrefConstant(className, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry with the + * given class name, method name, and descriptor. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(String className, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + return addInterfaceMethodrefConstant(addClassConstant(className, referencedClass), + nameAndTypeIndex, + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry for the + * given class and method. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(Clazz referencedClass, + Member referencedMember) + { + return addInterfaceMethodrefConstant(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry with the + * given class constant pool entry index, method name, and descriptor. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(int classIndex, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addInterfaceMethodrefConstant(classIndex, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a InterfaceMethodrefConstant constant pool entry with the + * given class constant pool entry index and name and type constant pool + * entry index. + * @return the constant pool index of the InterfaceMethodrefConstant. + */ + public int addInterfaceMethodrefConstant(int classIndex, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_InterfaceMethodref) + { + InterfaceMethodrefConstant methodrefConstant = (InterfaceMethodrefConstant)constant; + if (methodrefConstant.u2classIndex == classIndex && + methodrefConstant.u2nameAndTypeIndex == nameAndTypeIndex) + { + return index; + } + } + } + + return addConstant(new InterfaceMethodrefConstant(classIndex, + nameAndTypeIndex, + referencedClass, + referencedMember)); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry for the given + * class and method. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(Clazz referencedClass, + Member referencedMember) + { + return addMethodrefConstant(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry with the given + * class name, method name, and descriptor. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addMethodrefConstant(className, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry with the given + * class name, method name, and descriptor. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(String className, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + return addMethodrefConstant(addClassConstant(className, referencedClass), + nameAndTypeIndex, + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry with the given + * class constant pool entry index, method name, and descriptor. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(int classIndex, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return addMethodrefConstant(classIndex, + addNameAndTypeConstant(name, descriptor), + referencedClass, + referencedMember); + } + + + /** + * Finds or creates a MethodrefConstant constant pool entry with the given + * class constant pool entry index and name and type constant pool entry + * index. + * @return the constant pool index of the MethodrefConstant. + */ + public int addMethodrefConstant(int classIndex, + int nameAndTypeIndex, + Clazz referencedClass, + Member referencedMember) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_Methodref) + { + MethodrefConstant methodrefConstant = (MethodrefConstant)constant; + if (methodrefConstant.u2classIndex == classIndex && + methodrefConstant.u2nameAndTypeIndex == nameAndTypeIndex) + { + return index; + } + } + } + + return addConstant(new MethodrefConstant(classIndex, + nameAndTypeIndex, + referencedClass, + referencedMember)); + } + + + /** + * Finds or creates a ClassConstant constant pool entry for the given class. + * @return the constant pool index of the ClassConstant. + */ + public int addClassConstant(Clazz referencedClass) + { + return addClassConstant(referencedClass.getName(), + referencedClass); + } + + + /** + * Finds or creates a ClassConstant constant pool entry with the given name. + * @return the constant pool index of the ClassConstant. + */ + public int addClassConstant(String name, + Clazz referencedClass) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_Class) + { + ClassConstant classConstant = (ClassConstant)constant; + if (classConstant.getName(targetClass).equals(name)) + { + return index; + } + } + } + + int nameIndex = addUtf8Constant(name); + + return addConstant(new ClassConstant(nameIndex, referencedClass)); + } + + + /** + * Finds or creates a MethodTypeConstant constant pool entry with the given + * type. + * @return the constant pool index of the MethodTypeConstant. + */ + public int addMethodTypeConstant(String type) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_MethodType) + { + MethodTypeConstant methodTypeConstant = (MethodTypeConstant)constant; + if (methodTypeConstant.getType(targetClass).equals(type)) + { + return index; + } + } + } + + return addConstant(new MethodTypeConstant(addUtf8Constant(type))); + } + + + /** + * Finds or creates a NameAndTypeConstant constant pool entry with the given + * name and type. + * @return the constant pool index of the NameAndTypeConstant. + */ + public int addNameAndTypeConstant(String name, + String type) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_NameAndType) + { + NameAndTypeConstant nameAndTypeConstant = (NameAndTypeConstant)constant; + if (nameAndTypeConstant.getName(targetClass).equals(name) && + nameAndTypeConstant.getType(targetClass).equals(type)) + { + return index; + } + } + } + + return addConstant(new NameAndTypeConstant(addUtf8Constant(name), + addUtf8Constant(type))); + } + + + /** + * Finds or creates a Utf8Constant constant pool entry for the given string. + * @return the constant pool index of the Utf8Constant. + */ + public int addUtf8Constant(String string) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.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_Utf8) + { + Utf8Constant utf8Constant = (Utf8Constant)constant; + if (utf8Constant.getString().equals(string)) + { + return index; + } + } + } + + return addConstant(new Utf8Constant(string)); + } + + + /** + * Adds a given constant pool entry to the end of the constant pool/ + * @return the constant pool index for the added entry. + */ + public int addConstant(Constant constant) + { + int constantPoolCount = targetClass.u2constantPoolCount; + Constant[] constantPool = targetClass.constantPool; + + // Make sure there is enough space for another constant pool entry. + if (constantPool.length < constantPoolCount+2) + { + targetClass.constantPool = new Constant[constantPoolCount+2]; + System.arraycopy(constantPool, 0, + targetClass.constantPool, 0, + constantPoolCount); + constantPool = targetClass.constantPool; + } + + if (DEBUG) + { + System.out.println(targetClass.getName()+": adding ["+constant.getClass().getName()+"] at index "+targetClass.u2constantPoolCount); + } + + // Create a new Utf8Constant for the given string. + constantPool[targetClass.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[targetClass.u2constantPoolCount++] = null; + } + + return constantPoolCount; + } +} diff --git a/src/proguard/classfile/editor/ConstantPoolRemapper.java b/src/proguard/classfile/editor/ConstantPoolRemapper.java new file mode 100644 index 000000000..eaf7653b2 --- /dev/null +++ b/src/proguard/classfile/editor/ConstantPoolRemapper.java @@ -0,0 +1,662 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 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 +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + BootstrapMethodInfoVisitor, + InnerClassesInfoVisitor, + ExceptionInfoVisitor, + InstructionVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true); + + private int[] constantIndexMap; + + + /** + * Sets the given mapping of old constant pool entry indexes to their new + * indexes. + */ + public void setConstantIndexMap(int[] constantIndexMap) + { + this.constantIndexMap = constantIndexMap; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Remap the local constant pool references. + programClass.u2thisClass = remapConstantIndex(programClass.u2thisClass); + programClass.u2superClass = remapConstantIndex(programClass.u2superClass); + + remapConstantIndexArray(programClass.u2interfaces, + programClass.u2interfacesCount); + + // Remap the references of the contant pool entries themselves. + programClass.constantPoolEntriesAccept(this); + + // Remap the references in all fields, methods, and attributes. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + // Nothing to do. + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + // Nothing to do. + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + // Nothing to do. + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + // Nothing to do. + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + stringConstant.u2stringIndex = + remapConstantIndex(stringConstant.u2stringIndex); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + // Nothing to do. + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + invokeDynamicConstant.u2nameAndTypeIndex = + remapConstantIndex(invokeDynamicConstant.u2nameAndTypeIndex); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + methodHandleConstant.u2referenceIndex = + remapConstantIndex(methodHandleConstant.u2referenceIndex); + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + fieldrefConstant.u2classIndex = + remapConstantIndex(fieldrefConstant.u2classIndex); + fieldrefConstant.u2nameAndTypeIndex = + remapConstantIndex(fieldrefConstant.u2nameAndTypeIndex); + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + interfaceMethodrefConstant.u2classIndex = + remapConstantIndex(interfaceMethodrefConstant.u2classIndex); + interfaceMethodrefConstant.u2nameAndTypeIndex = + remapConstantIndex(interfaceMethodrefConstant.u2nameAndTypeIndex); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + methodrefConstant.u2classIndex = + remapConstantIndex(methodrefConstant.u2classIndex); + methodrefConstant.u2nameAndTypeIndex = + remapConstantIndex(methodrefConstant.u2nameAndTypeIndex); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.u2nameIndex = + remapConstantIndex(classConstant.u2nameIndex); + } + + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + methodTypeConstant.u2descriptorIndex = + remapConstantIndex(methodTypeConstant.u2descriptorIndex); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + nameAndTypeConstant.u2nameIndex = + remapConstantIndex(nameAndTypeConstant.u2nameIndex); + nameAndTypeConstant.u2descriptorIndex = + remapConstantIndex(nameAndTypeConstant.u2descriptorIndex); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + visitMember(programClass, programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + visitMember(programClass, programMethod); + } + + + private void visitMember(ProgramClass programClass, ProgramMember programMember) + { + // Remap the local constant pool references. + programMember.u2nameIndex = + remapConstantIndex(programMember.u2nameIndex); + programMember.u2descriptorIndex = + remapConstantIndex(programMember.u2descriptorIndex); + + // Remap the constant pool references of the remaining attributes. + programMember.attributesAccept(programClass, this); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + // Library classes are left unchanged. + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + // Library classes are left unchanged. + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + unknownAttribute.u2attributeNameIndex = + remapConstantIndex(unknownAttribute.u2attributeNameIndex); + + // There's not much else we can do with unknown attributes. + } + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + bootstrapMethodsAttribute.u2attributeNameIndex = + remapConstantIndex(bootstrapMethodsAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the bootstrap method entries. + bootstrapMethodsAttribute.bootstrapMethodEntriesAccept(clazz, this); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + sourceFileAttribute.u2attributeNameIndex = + remapConstantIndex(sourceFileAttribute.u2attributeNameIndex); + sourceFileAttribute.u2sourceFileIndex = + remapConstantIndex(sourceFileAttribute.u2sourceFileIndex); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + sourceDirAttribute.u2attributeNameIndex = + remapConstantIndex(sourceDirAttribute.u2attributeNameIndex); + sourceDirAttribute.u2sourceDirIndex = + remapConstantIndex(sourceDirAttribute.u2sourceDirIndex); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + innerClassesAttribute.u2attributeNameIndex = + remapConstantIndex(innerClassesAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the inner classes. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + enclosingMethodAttribute.u2attributeNameIndex = + remapConstantIndex(enclosingMethodAttribute.u2attributeNameIndex); + enclosingMethodAttribute.u2classIndex = + remapConstantIndex(enclosingMethodAttribute.u2classIndex); + enclosingMethodAttribute.u2nameAndTypeIndex = + remapConstantIndex(enclosingMethodAttribute.u2nameAndTypeIndex); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + deprecatedAttribute.u2attributeNameIndex = + remapConstantIndex(deprecatedAttribute.u2attributeNameIndex); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + syntheticAttribute.u2attributeNameIndex = + remapConstantIndex(syntheticAttribute.u2attributeNameIndex); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + signatureAttribute.u2attributeNameIndex = + remapConstantIndex(signatureAttribute.u2attributeNameIndex); + signatureAttribute.u2signatureIndex = + remapConstantIndex(signatureAttribute.u2signatureIndex); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + constantValueAttribute.u2attributeNameIndex = + remapConstantIndex(constantValueAttribute.u2attributeNameIndex); + constantValueAttribute.u2constantValueIndex = + remapConstantIndex(constantValueAttribute.u2constantValueIndex); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + exceptionsAttribute.u2attributeNameIndex = + remapConstantIndex(exceptionsAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the exceptions. + remapConstantIndexArray(exceptionsAttribute.u2exceptionIndexTable, + exceptionsAttribute.u2exceptionIndexTableLength); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.u2attributeNameIndex = + remapConstantIndex(codeAttribute.u2attributeNameIndex); + + // Initially, the code attribute editor doesn't contain any changes. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // 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 visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + stackMapAttribute.u2attributeNameIndex = + remapConstantIndex(stackMapAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the stack map frames. + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + stackMapTableAttribute.u2attributeNameIndex = + remapConstantIndex(stackMapTableAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the stack map frames. + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + lineNumberTableAttribute.u2attributeNameIndex = + remapConstantIndex(lineNumberTableAttribute.u2attributeNameIndex); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + localVariableTableAttribute.u2attributeNameIndex = + remapConstantIndex(localVariableTableAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + localVariableTypeTableAttribute.u2attributeNameIndex = + remapConstantIndex(localVariableTypeTableAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the local variables. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + annotationsAttribute.u2attributeNameIndex = + remapConstantIndex(annotationsAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + parameterAnnotationsAttribute.u2attributeNameIndex = + remapConstantIndex(parameterAnnotationsAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + annotationDefaultAttribute.u2attributeNameIndex = + remapConstantIndex(annotationDefaultAttribute.u2attributeNameIndex); + + // Remap the constant pool references of the annotations. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + bootstrapMethodInfo.u2methodHandleIndex = + remapConstantIndex(bootstrapMethodInfo.u2methodHandleIndex); + + // Remap the constant pool references of the bootstrap methods.. + remapConstantIndexArray(bootstrapMethodInfo.u2methodArguments, + bootstrapMethodInfo.u2methodArgumentCount); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + if (innerClassesInfo.u2innerClassIndex != 0) + { + innerClassesInfo.u2innerClassIndex = + remapConstantIndex(innerClassesInfo.u2innerClassIndex); + } + + if (innerClassesInfo.u2outerClassIndex != 0) + { + innerClassesInfo.u2outerClassIndex = + remapConstantIndex(innerClassesInfo.u2outerClassIndex); + } + + if (innerClassesInfo.u2innerNameIndex != 0) + { + innerClassesInfo.u2innerNameIndex = + remapConstantIndex(innerClassesInfo.u2innerNameIndex); + } + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + if (exceptionInfo.u2catchType != 0) + { + 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); + + 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(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + localVariableInfo.u2nameIndex = + remapConstantIndex(localVariableInfo.u2nameIndex); + localVariableInfo.u2descriptorIndex = + remapConstantIndex(localVariableInfo.u2descriptorIndex); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + localVariableTypeInfo.u2nameIndex = + remapConstantIndex(localVariableTypeInfo.u2nameIndex); + localVariableTypeInfo.u2signatureIndex = + remapConstantIndex(localVariableTypeInfo.u2signatureIndex); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + annotation.u2typeIndex = + remapConstantIndex(annotation.u2typeIndex); + + // Remap the constant pool references of the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + constantElementValue.u2elementNameIndex = + remapConstantIndex(constantElementValue.u2elementNameIndex); + constantElementValue.u2constantValueIndex = + remapConstantIndex(constantElementValue.u2constantValueIndex); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + enumConstantElementValue.u2elementNameIndex = + remapConstantIndex(enumConstantElementValue.u2elementNameIndex); + enumConstantElementValue.u2typeNameIndex = + remapConstantIndex(enumConstantElementValue.u2typeNameIndex); + enumConstantElementValue.u2constantNameIndex = + remapConstantIndex(enumConstantElementValue.u2constantNameIndex); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + classElementValue.u2elementNameIndex = + remapConstantIndex(classElementValue.u2elementNameIndex); + classElementValue.u2classInfoIndex = + remapConstantIndex(classElementValue.u2classInfoIndex); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + annotationElementValue.u2elementNameIndex = + remapConstantIndex(annotationElementValue.u2elementNameIndex); + + // Remap the constant pool references of the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + arrayElementValue.u2elementNameIndex = + remapConstantIndex(arrayElementValue.u2elementNameIndex); + + // Remap the constant pool references of the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Remaps all constant pool indices in the given array. + */ + private void remapConstantIndexArray(int[] array, int length) + { + for (int index = 0; index < length; index++) + { + array[index] = remapConstantIndex(array[index]); + } + } + + + /** + * Returns the new constant pool index of the entry at the + * given index. + */ + private int remapConstantIndex(int constantIndex) + { + return constantIndexMap[constantIndex]; + } +} diff --git a/src/proguard/classfile/editor/ConstantPoolShrinker.java b/src/proguard/classfile/editor/ConstantPoolShrinker.java new file mode 100644 index 000000000..ee309a010 --- /dev/null +++ b/src/proguard/classfile/editor/ConstantPoolShrinker.java @@ -0,0 +1,578 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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.*; + +import java.util.Arrays; + +/** + * This ClassVisitor removes all unused entries from the constant pool. + * + * @author Eric Lafortune + */ +public class ConstantPoolShrinker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor, + AttributeVisitor, + BootstrapMethodInfoVisitor, + InnerClassesInfoVisitor, + ExceptionInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor, + InstructionVisitor +{ + // A visitor info flag to indicate the constant is being used. + private static final Object USED = new Object(); + + private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Mark this class's name. + markConstant(programClass, programClass.u2thisClass); + + // Mark the superclass class constant. + programClass.superClassConstantAccept(this); + + // Mark the interface class constants. + programClass.interfaceConstantsAccept(this); + + // Mark the constants referenced by the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Mark the attributes. + programClass.attributesAccept(this); + + // Shift the used constant pool entries together, filling out the + // index map. + int newConstantPoolCount = + shrinkConstantPool(programClass.constantPool, + programClass.u2constantPoolCount); + + // Remap the references to the constant pool if it has shrunk. + if (newConstantPoolCount < programClass.u2constantPoolCount) + { + programClass.u2constantPoolCount = newConstantPoolCount; + + // Remap all constant pool references. + constantPoolRemapper.setConstantIndexMap(constantIndexMap); + constantPoolRemapper.visitProgramClass(programClass); + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Mark the name and descriptor. + markConstant(programClass, programMember.u2nameIndex); + markConstant(programClass, programMember.u2descriptorIndex); + + // Mark the attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + markAsUsed(constant); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + markAsUsed(stringConstant); + + markConstant(clazz, stringConstant.u2stringIndex); + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + markAsUsed(invokeDynamicConstant); + + markConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex); + + // Mark the bootstrap methods attribute. + clazz.attributesAccept(this); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + markAsUsed(methodHandleConstant); + + markConstant(clazz, methodHandleConstant.u2referenceIndex); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + markAsUsed(refConstant); + + markConstant(clazz, refConstant.u2classIndex); + markConstant(clazz, refConstant.u2nameAndTypeIndex); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + markAsUsed(classConstant); + + markConstant(clazz, classConstant.u2nameIndex); + } + + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + markAsUsed(methodTypeConstant); + + markConstant(clazz, methodTypeConstant.u2descriptorIndex); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + markAsUsed(nameAndTypeConstant); + + markConstant(clazz, nameAndTypeConstant.u2nameIndex); + markConstant(clazz, nameAndTypeConstant.u2descriptorIndex); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + markConstant(clazz, attribute.u2attributeNameIndex); + } + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + markConstant(clazz, bootstrapMethodsAttribute.u2attributeNameIndex); + + // Mark the bootstrap method entries. + bootstrapMethodsAttribute.bootstrapMethodEntriesAccept(clazz, this); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + markConstant(clazz, sourceFileAttribute.u2attributeNameIndex); + markConstant(clazz, sourceFileAttribute.u2sourceFileIndex); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + markConstant(clazz, sourceDirAttribute.u2attributeNameIndex); + markConstant(clazz, sourceDirAttribute.u2sourceDirIndex); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + markConstant(clazz, innerClassesAttribute.u2attributeNameIndex); + + // Mark the outer class entries. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + markConstant(clazz, enclosingMethodAttribute.u2attributeNameIndex); + markConstant(clazz, enclosingMethodAttribute.u2classIndex); + + if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) + { + markConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex); + } + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + markConstant(clazz, signatureAttribute.u2attributeNameIndex); + markConstant(clazz, signatureAttribute.u2signatureIndex); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + markConstant(clazz, constantValueAttribute.u2attributeNameIndex); + markConstant(clazz, constantValueAttribute.u2constantValueIndex); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + markConstant(clazz, exceptionsAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the exceptions. + exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + markConstant(clazz, codeAttribute.u2attributeNameIndex); + + // 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 visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + markConstant(clazz, stackMapAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the stack map frames. + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + markConstant(clazz, stackMapTableAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the stack map frames. + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + markConstant(clazz, localVariableTableAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + markConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the local variable types. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + markConstant(clazz, annotationsAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + markConstant(clazz, parameterAnnotationsAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + markConstant(clazz, annotationDefaultAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the element value. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + markConstant(clazz, bootstrapMethodInfo.u2methodHandleIndex); + + // Mark the constant pool entries referenced by the arguments. + bootstrapMethodInfo.methodArgumentsAccept(clazz, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + innerClassesInfo.innerClassConstantAccept(clazz, this); + innerClassesInfo.outerClassConstantAccept(clazz, this); + innerClassesInfo.innerNameConstantAccept(clazz, this); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + if (exceptionInfo.u2catchType != 0) + { + markConstant(clazz, exceptionInfo.u2catchType); + } + } + + + // 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) + { + // Mark the constant pool entries referenced by the verification types. + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + // Mark the constant pool entries referenced by the verification types. + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + // Mark the constant pool entries referenced by 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) + { + markConstant(clazz, objectType.u2classIndex); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + markConstant(clazz, localVariableInfo.u2nameIndex); + markConstant(clazz, localVariableInfo.u2descriptorIndex); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + 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); + } + + 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 visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + markConstant(clazz, constantInstruction.constantIndex); + } + + + // Small utility methods. + + /** + * Marks the given constant pool entry of the given class. This includes + * visiting any referenced objects. + */ + private void markConstant(Clazz clazz, int index) + { + clazz.constantPoolEntryAccept(index, this); + } + + + /** + * Marks the given visitor accepter as being used. + */ + private void markAsUsed(Constant constant) + { + constant.setVisitorInfo(USED); + } + + + /** + * Returns whether the given visitor accepter has been marked as being used. + */ + private boolean isUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == USED; + } + + + /** + * Removes all constants 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) + { + // Create a new index map, if necessary. + 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 = isUsed(constant); + } + + if (isUsed) + { + constantPool[counter++] = constant; + } + } + + // Clear the remaining constant pool elements. + Arrays.fill(constantPool, counter, length, null); + + return counter; + } +} diff --git a/src/proguard/classfile/editor/ConstantPoolSorter.java b/src/proguard/classfile/editor/ConstantPoolSorter.java new file mode 100644 index 000000000..b578624de --- /dev/null +++ b/src/proguard/classfile/editor/ConstantPoolSorter.java @@ -0,0 +1,123 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.Constant; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.Arrays; + +/** + * This ClassVisitor sorts the constant pool entries of the program 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 +extends SimplifiedVisitor +implements ClassVisitor +{ + private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + private ComparableConstant[] comparableConstantPool = new ComparableConstant[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + private Constant[] newConstantPool = new Constant[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + + private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + int constantPoolCount = programClass.u2constantPoolCount; + + // Sort the constant pool and set up an index map. + if (constantIndexMap.length < constantPoolCount) + { + constantIndexMap = new int[constantPoolCount]; + comparableConstantPool = new ComparableConstant[constantPoolCount]; + newConstantPool = new Constant[constantPoolCount]; + } + + // Initialize an array whose elements can be compared. + int sortLength = 0; + for (int oldIndex = 1; oldIndex < constantPoolCount; oldIndex++) + { + Constant constant = programClass.constantPool[oldIndex]; + if (constant != null) + { + comparableConstantPool[sortLength++] = + new ComparableConstant(programClass, oldIndex, constant); + } + } + + // Sort the array. + Arrays.sort(comparableConstantPool, 0, sortLength); + + // Save the sorted elements. + int newLength = 1; + int newIndex = 1; + ComparableConstant previousComparableConstant = null; + for (int sortIndex = 0; sortIndex < sortLength; sortIndex++) + { + ComparableConstant comparableConstant = comparableConstantPool[sortIndex]; + + // Isn't this a duplicate of the previous constant? + if (!comparableConstant.equals(previousComparableConstant)) + { + // Remember the index of the new entry. + newIndex = newLength; + + // Copy the sorted constant pool entry over to the constant pool. + Constant constant = comparableConstant.getConstant(); + + newConstantPool[newLength++] = constant; + + // Long entries take up two slots, the second of which is null. + int tag = constant.getTag(); + if (tag == ClassConstants.CONSTANT_Long || + tag == ClassConstants.CONSTANT_Double) + { + newConstantPool[newLength++] = null; + } + + previousComparableConstant = comparableConstant; + } + + // Fill out the map array. + constantIndexMap[comparableConstant.getIndex()] = newIndex; + } + + // Copy the new constant pool over. + System.arraycopy(newConstantPool, 0, programClass.constantPool, 0, newLength); + + // Clear any remaining entries. + Arrays.fill(programClass.constantPool, newLength, constantPoolCount, null); + + programClass.u2constantPoolCount = newLength; + + // Remap all constant pool references. + constantPoolRemapper.setConstantIndexMap(constantIndexMap); + constantPoolRemapper.visitProgramClass(programClass); + } +} diff --git a/src/proguard/classfile/editor/ElementValueAdder.java b/src/proguard/classfile/editor/ElementValueAdder.java new file mode 100644 index 000000000..9c8b3f943 --- /dev/null +++ b/src/proguard/classfile/editor/ElementValueAdder.java @@ -0,0 +1,217 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; + +/** + * This AnnotationVisitor adds all element values that it visits to the given + * target annotation default attribute, annotation, or element value. + * + * @author Eric Lafortune + */ +public class ElementValueAdder +implements ElementValueVisitor +{ + private static final ElementValue[] EMPTY_ELEMENT_VALUES = new ElementValue[0]; + + + private final ProgramClass targetClass; + private final AnnotationDefaultAttribute targetAnnotationDefaultAttribute; + + private final ConstantAdder constantAdder; + private final ElementValuesEditor elementValuesEditor; + + + /** + * Creates a new ElementValueAdder that will copy element values into the + * given target annotation default attribute value. + */ + public ElementValueAdder(ProgramClass targetClass, + AnnotationDefaultAttribute targetAnnotationDefaultAttribute, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotationDefaultAttribute = targetAnnotationDefaultAttribute; + + constantAdder = new ConstantAdder(targetClass); + elementValuesEditor = null; + } + + + /** + * Creates a new ElementValueAdder that will copy element values into the + * given target annotation. + */ + public ElementValueAdder(ProgramClass targetClass, + Annotation targetAnnotation, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotationDefaultAttribute = null; + + constantAdder = new ConstantAdder(targetClass); + elementValuesEditor = new ElementValuesEditor(targetClass, + targetAnnotation, + replaceElementValues); + } + + + /** + * Creates a new ElementValueAdder that will copy element values into the + * given target element value. + */ + public ElementValueAdder(ProgramClass targetClass, + ArrayElementValue targetArrayElementValue, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotationDefaultAttribute = null; + + constantAdder = new ConstantAdder(targetClass); + elementValuesEditor = new ElementValuesEditor(targetClass, + targetArrayElementValue, + replaceElementValues); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + // Create a copy of the element value. + ConstantElementValue newConstantElementValue = + new ConstantElementValue(constantElementValue.u1tag, + constantElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, constantElementValue.u2elementNameIndex), + constantAdder.addConstant(clazz, constantElementValue.u2constantValueIndex)); + + newConstantElementValue.referencedClass = constantElementValue.referencedClass; + newConstantElementValue.referencedMethod = constantElementValue.referencedMethod; + + // Add it to the target. + addElementValue(newConstantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + // Create a copy of the element value. + EnumConstantElementValue newEnumConstantElementValue = + new EnumConstantElementValue(enumConstantElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, enumConstantElementValue.u2elementNameIndex), + constantAdder.addConstant(clazz, enumConstantElementValue.u2typeNameIndex), + constantAdder.addConstant(clazz, enumConstantElementValue.u2constantNameIndex)); + + newEnumConstantElementValue.referencedClass = enumConstantElementValue.referencedClass; + newEnumConstantElementValue.referencedMethod = enumConstantElementValue.referencedMethod; + + // TODO: Clone array. + newEnumConstantElementValue.referencedClasses = enumConstantElementValue.referencedClasses; + + // Add it to the target. + addElementValue(newEnumConstantElementValue); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + // Create a copy of the element value. + ClassElementValue newClassElementValue = + new ClassElementValue(classElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, classElementValue.u2elementNameIndex), + constantAdder.addConstant(clazz, classElementValue.u2classInfoIndex)); + + newClassElementValue.referencedClass = classElementValue.referencedClass; + newClassElementValue.referencedMethod = classElementValue.referencedMethod; + + // TODO: Clone array. + newClassElementValue.referencedClasses = classElementValue.referencedClasses; + + // Add it to the target. + addElementValue(newClassElementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + // Create a copy of the element value. + AnnotationElementValue newAnnotationElementValue = + new AnnotationElementValue(annotationElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, annotationElementValue.u2elementNameIndex), + new Annotation()); + + newAnnotationElementValue.referencedClass = annotationElementValue.referencedClass; + newAnnotationElementValue.referencedMethod = annotationElementValue.referencedMethod; + + annotationElementValue.annotationAccept(clazz, + new AnnotationAdder(targetClass, + newAnnotationElementValue)); + + // Add it to the target. + addElementValue(newAnnotationElementValue); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + // Create a copy of the element value. + ArrayElementValue newArrayElementValue = + new ArrayElementValue(arrayElementValue.u2elementNameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, arrayElementValue.u2elementNameIndex), + 0, + arrayElementValue.u2elementValuesCount > 0 ? + new ElementValue[arrayElementValue.u2elementValuesCount] : + EMPTY_ELEMENT_VALUES); + + newArrayElementValue.referencedClass = arrayElementValue.referencedClass; + newArrayElementValue.referencedMethod = arrayElementValue.referencedMethod; + + arrayElementValue.elementValuesAccept(clazz, + annotation, + new ElementValueAdder(targetClass, + newArrayElementValue, + false)); + + // Add it to the target. + addElementValue(newArrayElementValue); + } + + + // Small utility methods. + + private void addElementValue(ElementValue newElementValue) + { + // What's the target? + if (targetAnnotationDefaultAttribute != null) + { + // Simply set the completed element value. + targetAnnotationDefaultAttribute.defaultValue = newElementValue; + } + else + { + // Add it to the target. + elementValuesEditor.addElementValue(newElementValue); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/ElementValuesEditor.java b/src/proguard/classfile/editor/ElementValuesEditor.java new file mode 100644 index 000000000..57671e6ac --- /dev/null +++ b/src/proguard/classfile/editor/ElementValuesEditor.java @@ -0,0 +1,238 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.*; + +/** + * This class can add and delete element values to and from a given target + * annotation default attribute, annotation, or array element value. Element + * values to be added must be filled out beforehand, including their references + * to the constant pool. + * + * @author Eric Lafortune + */ +public class ElementValuesEditor +{ + private final ProgramClass targetClass; + private final Annotation targetAnnotation; + private final ArrayElementValue targetArrayElementValue; + private final boolean replaceElementValues; + + + /** + * Creates a new ElementValuesEditor that will edit element values in the + * given target annotation. + */ + public ElementValuesEditor(ProgramClass targetClass, + Annotation targetAnnotation, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotation = targetAnnotation; + this.targetArrayElementValue = null; + this.replaceElementValues = replaceElementValues; + } + + + /** + * Creates a new ElementValuesEditor that will edit element values in the + * given target array element value. + */ + public ElementValuesEditor(ProgramClass targetClass, + ArrayElementValue targetArrayElementValue, + boolean replaceElementValues) + { + this.targetClass = targetClass; + this.targetAnnotation = null; + this.targetArrayElementValue = targetArrayElementValue; + this.replaceElementValues = replaceElementValues; + } + + + /** + * Adds the given elementValue to the target. + */ + public void addElementValue(ElementValue elementValue) + { + // What's the target? + if (targetAnnotation != null) + { + // Try to replace an existing element value. + if (!replaceElementValues || + !replaceElementValue(targetAnnotation.u2elementValuesCount, + targetAnnotation.elementValues, + elementValue)) + { + // Otherwise append the element value. + targetAnnotation.elementValues = + addElementValue(targetAnnotation.u2elementValuesCount, + targetAnnotation.elementValues, + elementValue); + + targetAnnotation.u2elementValuesCount++; + } + } + else + { + // Try to replace an existing element value. + if (!replaceElementValues || + !replaceElementValue(targetArrayElementValue.u2elementValuesCount, + targetArrayElementValue.elementValues, + elementValue)) + { + // Otherwise append the element value. + targetArrayElementValue.elementValues = + addElementValue(targetArrayElementValue.u2elementValuesCount, + targetArrayElementValue.elementValues, + elementValue); + + targetArrayElementValue.u2elementValuesCount++; + } + } + } + + + /** + * Deletes the given elementValue to the target. + */ + public void deleteElementValue(String elementValueMethodName) + { + // What's the target? + if (targetAnnotation != null) + { + // Delete the element value to the target annotation. + targetAnnotation.u2elementValuesCount = + deleteElementValue(targetAnnotation.u2elementValuesCount, + targetAnnotation.elementValues, + elementValueMethodName); + } + else + { + // Delete the element value to the target array element value. + targetArrayElementValue.u2elementValuesCount = + deleteElementValue(targetArrayElementValue.u2elementValuesCount, + targetArrayElementValue.elementValues, + elementValueMethodName); + } + } + + + // Small utility methods. + + /** + * Tries put the given element value in place of an existing element value + * of the same name, returning whether it was present. + */ + private boolean replaceElementValue(int elementValuesCount, + ElementValue[] elementValues, + ElementValue elementValue) + { + // Find the element value with the same name. + int index = findElementValue(elementValuesCount, + elementValues, + elementValue.getMethodName(targetClass)); + if (index < 0) + { + return false; + } + + elementValues[index] = elementValue; + + return true; + } + + + /** + * Appends the given element value to the given array of element values, + * creating a new array if necessary. + */ + private ElementValue[] addElementValue(int elementValuesCount, + ElementValue[] elementValues, + ElementValue elementValue) + { + // Is the array too small to contain the additional elementValue? + if (elementValues.length <= elementValuesCount) + { + // Create a new array and copy the elementValues into it. + ElementValue[] newElementValues = new ElementValue[elementValuesCount + 1]; + System.arraycopy(elementValues, 0, + newElementValues, 0, + elementValuesCount); + elementValues = newElementValues; + } + + // Append the elementValue. + elementValues[elementValuesCount] = elementValue; + + return elementValues; + } + + + /** + * Deletes the element values with the given name from the given array of + * element values, returning the new number of element values. + */ + private int deleteElementValue(int elementValuesCount, + ElementValue[] elementValues, + String elementValueMethodName) + { + // Find the element value. + int index = findElementValue(elementValuesCount, + elementValues, + elementValueMethodName); + if (index < 0) + { + return elementValuesCount; + } + + // Shift the other element values in the array. + System.arraycopy(elementValues, index + 1, + elementValues, index, + elementValuesCount - index - 1); + + // Clear the last entry in the array. + elementValues[--elementValuesCount] = null; + + return elementValuesCount; + } + + + /** + * Finds the index of the element value with the given name in the given + * array of element values. + */ + private int findElementValue(int elementValuesCount, + ElementValue[] elementValues, + String elementValueName) + { + for (int index = 0; index < elementValuesCount; index++) + { + if (elementValues[index].getMethodName(targetClass).equals(elementValueName)) + { + return index; + } + } + + return -1; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/ExceptionAdder.java b/src/proguard/classfile/editor/ExceptionAdder.java new file mode 100644 index 000000000..152a06564 --- /dev/null +++ b/src/proguard/classfile/editor/ExceptionAdder.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ConstantAdder constantAdder; + private final ExceptionsAttributeEditor exceptionsAttributeEditor; + + + /** + * Creates a new ExceptionAdder that will copy classes into the given + * target exceptions attribute. + */ + public ExceptionAdder(ProgramClass targetClass, + ExceptionsAttribute targetExceptionsAttribute) + { + constantAdder = new ConstantAdder(targetClass); + exceptionsAttributeEditor = new ExceptionsAttributeEditor(targetExceptionsAttribute); + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Add a class constant to the constant pool. + constantAdder.visitClassConstant(clazz, classConstant); + + // Add the index of the class constant to the list of exceptions. + exceptionsAttributeEditor.addException(constantAdder.getConstantIndex()); + } +} diff --git a/src/proguard/classfile/editor/ExceptionInfoAdder.java b/src/proguard/classfile/editor/ExceptionInfoAdder.java new file mode 100644 index 000000000..c1c20fa5a --- /dev/null +++ b/src/proguard/classfile/editor/ExceptionInfoAdder.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor adds all exception information that it visits to + * the given target code attribute. + * + * @author Eric Lafortune + */ +public class ExceptionInfoAdder +implements ExceptionInfoVisitor +{ + private final ConstantAdder constantAdder; + private final CodeAttributeComposer codeAttributeComposer; + + + /** + * Creates a new ExceptionAdder that will copy exceptions into the given + * target code attribute. + */ + public ExceptionInfoAdder(ProgramClass targetClass, + CodeAttributeComposer targetComposer) + { + constantAdder = new ConstantAdder(targetClass); + codeAttributeComposer = targetComposer; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + // Create a copy of the exception info. + ExceptionInfo newExceptionInfo = + new ExceptionInfo(exceptionInfo.u2startPC, + exceptionInfo.u2endPC, + exceptionInfo.u2handlerPC, + exceptionInfo.u2catchType == 0 ? 0 : + constantAdder.addConstant(clazz, exceptionInfo.u2catchType)); + + // Add the completed exception info. + codeAttributeComposer.appendException(newExceptionInfo); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/ExceptionsAttributeEditor.java b/src/proguard/classfile/editor/ExceptionsAttributeEditor.java new file mode 100644 index 000000000..98bb79ebd --- /dev/null +++ b/src/proguard/classfile/editor/ExceptionsAttributeEditor.java @@ -0,0 +1,68 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 a given exceptions attribute. + * Exceptions to be added must have been added to the constant pool and filled + * out beforehand. + * + * @author Eric Lafortune + */ +public class ExceptionsAttributeEditor +{ + private ExceptionsAttribute targetExceptionsAttribute; + + + /** + * Creates a new ExceptionsAttributeEditor that will edit exceptions in the + * given exceptions attribute. + */ + public ExceptionsAttributeEditor(ExceptionsAttribute targetExceptionsAttribute) + { + this.targetExceptionsAttribute = targetExceptionsAttribute; + } + + + /** + * Adds a given exception to the exceptions attribute. + */ + public void addException(int exceptionIndex) + { + int exceptionIndexTableLength = targetExceptionsAttribute.u2exceptionIndexTableLength; + int[] exceptionIndexTable = targetExceptionsAttribute.u2exceptionIndexTable; + + // Make sure there is enough space for the new exception. + if (exceptionIndexTable.length <= exceptionIndexTableLength) + { + targetExceptionsAttribute.u2exceptionIndexTable = new int[exceptionIndexTableLength+1]; + System.arraycopy(exceptionIndexTable, 0, + targetExceptionsAttribute.u2exceptionIndexTable, 0, + exceptionIndexTableLength); + exceptionIndexTable = targetExceptionsAttribute.u2exceptionIndexTable; + } + + // Add the exception. + exceptionIndexTable[targetExceptionsAttribute.u2exceptionIndexTableLength++] = exceptionIndex; + } +} diff --git a/src/proguard/classfile/editor/InnerClassesAccessFixer.java b/src/proguard/classfile/editor/InnerClassesAccessFixer.java new file mode 100644 index 000000000..5a5e8a574 --- /dev/null +++ b/src/proguard/classfile/editor/InnerClassesAccessFixer.java @@ -0,0 +1,83 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.InnerClassesInfoVisitor; +import proguard.classfile.attribute.InnerClassesInfo; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +/** + * This InnerClassesInfoVisitor fixes the inner class access flags of the + * inner classes information that it visits. + * + * @author Eric Lafortune + */ +public class InnerClassesAccessFixer +extends SimplifiedVisitor +implements InnerClassesInfoVisitor, + ConstantVisitor, + ClassVisitor +{ + private int innerClassAccessFlags; + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + // The current access flags are the default. + innerClassAccessFlags = innerClassesInfo.u2innerClassAccessFlags; + + // See if we can find new access flags. + innerClassesInfo.innerClassConstantAccept(clazz, this); + + // Update the access flags. + innerClassesInfo.u2innerClassAccessFlags = innerClassAccessFlags; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.referencedClassAccept(this); + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + innerClassAccessFlags = + AccessUtil.replaceAccessFlags(innerClassAccessFlags, + programClass.u2accessFlags); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/InstructionAdder.java b/src/proguard/classfile/editor/InstructionAdder.java new file mode 100644 index 000000000..422a348fc --- /dev/null +++ b/src/proguard/classfile/editor/InstructionAdder.java @@ -0,0 +1,76 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.instruction.visitor.InstructionVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor adds all instructions that it visits to the given + * target code attribute. + * + * @author Eric Lafortune + */ +public class InstructionAdder +extends SimplifiedVisitor +implements InstructionVisitor +{ + private final ConstantAdder constantAdder; + private final CodeAttributeComposer codeAttributeComposer; + + + /** + * Creates a new InstructionAdder that will copy classes into the given + * target code attribute. + */ + public InstructionAdder(ProgramClass targetClass, + CodeAttributeComposer targetComposer) + { + constantAdder = new ConstantAdder(targetClass); + codeAttributeComposer = targetComposer; + } + + + // Implementations for InstructionVisitor. + + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Add the instruction. + codeAttributeComposer.appendInstruction(offset, instruction); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Create a copy of the instruction. + Instruction newConstantInstruction = + new ConstantInstruction(constantInstruction.opcode, + constantAdder.addConstant(clazz, constantInstruction.constantIndex), + constantInstruction.constant); + + // Add the instruction. + codeAttributeComposer.appendInstruction(offset, newConstantInstruction); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/InstructionWriter.java b/src/proguard/classfile/editor/InstructionWriter.java new file mode 100644 index 000000000..c4a9b094f --- /dev/null +++ b/src/proguard/classfile/editor/InstructionWriter.java @@ -0,0 +1,320 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 AttributeVisitor, + * it then applies the collected changes. The process will be repeated + * recursively, if necessary. The caller still has to update the frame sizes. + * + * @author Eric Lafortune + */ +public class InstructionWriter +extends SimplifiedVisitor +implements InstructionVisitor, + AttributeVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + public static boolean DEBUG = false; + //*/ + + + private int codeLength; + + private CodeAttributeEditor codeAttributeEditor; + + + /** + * Resets the accumulated code. + * @param codeLength the length of the code that will be edited next. + */ + public void reset(int codeLength) + { + this.codeLength = codeLength; + + if (codeAttributeEditor != null) + { + codeAttributeEditor.reset(codeLength); + } + } + + + /** + * Extends the size of the accumulated code. + * @param codeLength the length of the code that will be edited next. + */ + public void extend(int codeLength) + { + this.codeLength = codeLength; + + if (codeAttributeEditor != null) + { + codeAttributeEditor.extend(codeLength); + } + } + + + // Implementations for InstructionVisitor. + + 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(codeAttribute, offset); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + try + { + // Try to write out the instruction. + constantInstruction.write(codeAttribute, offset); + } + catch (IllegalArgumentException exception) + { + // Create a new constant instruction that will fit. + Instruction replacementInstruction = + new ConstantInstruction(constantInstruction.opcode, + constantInstruction.constantIndex, + constantInstruction.constant); + + if (DEBUG) + { + System.out.println(" "+constantInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString()); + } + + replaceInstruction(offset, replacementInstruction); + + // Write out a dummy constant instruction for now. + constantInstruction.constantIndex = 0; + constantInstruction.constant = 0; + constantInstruction.write(codeAttribute, offset); + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + try + { + // Try to write out the instruction. + variableInstruction.write(codeAttribute, offset); + } + catch (IllegalArgumentException exception) + { + // Create a new variable instruction that will fit. + Instruction replacementInstruction = + new VariableInstruction(variableInstruction.opcode, + variableInstruction.variableIndex, + variableInstruction.constant); + + replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + System.out.println(" "+variableInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString()); + } + + // Write out a dummy variable instruction for now. + variableInstruction.variableIndex = 0; + variableInstruction.constant = 0; + variableInstruction.write(codeAttribute, offset); + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + try + { + // Try to write out the instruction. + branchInstruction.write(codeAttribute, offset); + } + catch (IllegalArgumentException exception) + { + // Create a new unconditional branch that will fit. + Instruction replacementInstruction = + new BranchInstruction(InstructionConstants.OP_GOTO_W, + branchInstruction.branchOffset); + + // Create a new instruction that will fit. + switch (branchInstruction.opcode) + { + default: + { + // Create a new branch instruction that will fit. + replacementInstruction = + new BranchInstruction(branchInstruction.opcode, + branchInstruction.branchOffset); + + break; + } + + // Some special cases, for which a wide branch doesn't exist. + case InstructionConstants.OP_IFEQ: + case InstructionConstants.OP_IFNE: + case InstructionConstants.OP_IFLT: + case InstructionConstants.OP_IFGE: + case InstructionConstants.OP_IFGT: + case InstructionConstants.OP_IFLE: + case InstructionConstants.OP_IFICMPEQ: + case InstructionConstants.OP_IFICMPNE: + case InstructionConstants.OP_IFICMPLT: + case InstructionConstants.OP_IFICMPGE: + case InstructionConstants.OP_IFICMPGT: + case InstructionConstants.OP_IFICMPLE: + case InstructionConstants.OP_IFACMPEQ: + case InstructionConstants.OP_IFACMPNE: + { + // Insert the complementary conditional branch. + Instruction complementaryConditionalBranch = + new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1), + (1+2) + (1+4)); + + insertBeforeInstruction(offset, complementaryConditionalBranch); + + // Create a new unconditional branch that will fit. + break; + } + + case InstructionConstants.OP_IFNULL: + case InstructionConstants.OP_IFNONNULL: + { + // Insert the complementary conditional branch. + Instruction complementaryConditionalBranch = + new BranchInstruction((byte)(branchInstruction.opcode ^ 1), + (1+2) + (1+4)); + + insertBeforeInstruction(offset, complementaryConditionalBranch); + + // Create a new unconditional branch that will fit. + break; + } + } + + if (DEBUG) + { + System.out.println(" "+branchInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString()); + } + + replaceInstruction(offset, replacementInstruction); + + // Write out a dummy branch instruction for now. + branchInstruction.branchOffset = 0; + branchInstruction.write(codeAttribute, offset); + } + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + // Try to write out the instruction. + // Switch instructions should always fit. + switchInstruction.write(codeAttribute, offset); + } + + + // Implementations for AttributeVisitor. + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Avoid doing any work if nothing is changing anyway. + if (codeAttributeEditor != null) + { + if (DEBUG) + { + System.out.println("InstructionWriter: widening instructions in "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + } + + // Apply the collected expansions. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + + // Don't keep the editor around. We're assuming it won't be needed + // very often, so we don't want to be resetting it all the time. + codeAttributeEditor = null; + } + } + + + // Small utility methods. + + /** + * Remembers to place the given instruction right before the instruction + * at the given offset. + */ + private void insertBeforeInstruction(int instructionOffset, Instruction instruction) + { + ensureCodeAttributeEditor(); + + // Replace the instruction. + codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction); + } + + + /** + * Remembers to replace the instruction at the given offset by the given + * instruction. + */ + private void replaceInstruction(int instructionOffset, Instruction instruction) + { + ensureCodeAttributeEditor(); + + // Replace the instruction. + codeAttributeEditor.replaceInstruction(instructionOffset, instruction); + } + + + /** + * Remembers to place the given instruction right after the instruction + * at the given offset. + */ + private void insertAfterInstruction(int instructionOffset, Instruction instruction) + { + ensureCodeAttributeEditor(); + + // Replace the instruction. + codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction); + } + + + /** + * Makes sure there is a code attribute editor for the given code attribute. + */ + private void ensureCodeAttributeEditor() + { + if (codeAttributeEditor == null) + { + codeAttributeEditor = new CodeAttributeEditor(false, true); + codeAttributeEditor.reset(codeLength); + } + } +} diff --git a/src/proguard/classfile/editor/InterfaceAdder.java b/src/proguard/classfile/editor/InterfaceAdder.java new file mode 100644 index 000000000..1ad67dc9c --- /dev/null +++ b/src/proguard/classfile/editor/InterfaceAdder.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor adds all interfaces that it visits to the given + * target class. + * + * @author Eric Lafortune + */ +public class InterfaceAdder +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final ConstantAdder constantAdder; + private final InterfacesEditor interfacesEditor; + + + /** + * Creates a new InterfaceAdder that will add interfaces to the given + * target class. + */ + public InterfaceAdder(ProgramClass targetClass) + { + constantAdder = new ConstantAdder(targetClass); + interfacesEditor = new InterfacesEditor(targetClass); + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + interfacesEditor.addInterface(constantAdder.addConstant(clazz, classConstant)); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/InterfaceSorter.java b/src/proguard/classfile/editor/InterfaceSorter.java new file mode 100644 index 000000000..0d1f28ceb --- /dev/null +++ b/src/proguard/classfile/editor/InterfaceSorter.java @@ -0,0 +1,152 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.*; + +/** + * This ClassVisitor sorts the interfaces of the program classes that it visits. + * + * @author Eric Lafortune + */ +public class InterfaceSorter +extends SimplifiedVisitor +implements ClassVisitor, + AttributeVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + int[] interfaces = programClass.u2interfaces; + int interfacesCount = programClass.u2interfacesCount; + + if (interfacesCount > 1) + { + // Sort the interfaces. + Arrays.sort(interfaces, 0, interfacesCount); + + // Remove any duplicate entries. + int newInterfacesCount = 0; + int previousInterfaceIndex = 0; + for (int index = 0; index < interfacesCount; index++) + { + int interfaceIndex = interfaces[index]; + + // Isn't this a duplicate of the previous interface? + if (interfaceIndex != previousInterfaceIndex) + { + interfaces[newInterfacesCount++] = interfaceIndex; + + // Remember the interface. + previousInterfaceIndex = interfaceIndex; + } + } + + programClass.u2interfacesCount = newInterfacesCount; + + // Update the signature, if any + programClass.attributesAccept(this); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Process the generic definitions, superclass, and implemented + // interfaces. + String signature = clazz.getString(signatureAttribute.u2signatureIndex); + + // Count the signature types. + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(signature); + + int count = 0; + int interfacesCount = -1; + while (internalTypeEnumeration.hasMoreTypes()) + { + String internalType = internalTypeEnumeration.nextType(); + + count++; + + if (ClassUtil.isInternalClassType(internalType)) + { + interfacesCount++; + } + } + + // Put the signature types in an array. + internalTypeEnumeration = + new InternalTypeEnumeration(signature); + + String[] internalTypes = new String[count]; + + for (int index = 0; index < count; index++) + { + String internalType = internalTypeEnumeration.nextType(); + + internalTypes[index] = internalType; + } + + // Sort the interface types in the array. + Arrays.sort(internalTypes, count - interfacesCount, count); + + // Recompose the signature types in a string. + StringBuffer newSignatureBuffer = new StringBuffer(); + + for (int index = 0; index < count; index++) + { + // Is this not an interface type, or an interface type that isn't + // a duplicate of the previous interface type? + if (index < count - interfacesCount || + !internalTypes[index].equals(internalTypes[index-1])) + { + newSignatureBuffer.append(internalTypes[index]); + } + } + + String newSignature = newSignatureBuffer.toString(); + + // Did the signature change? + if (!newSignature.equals(signature)) + { + // Update the signature. + ((Utf8Constant)((ProgramClass)clazz).constantPool[signatureAttribute.u2signatureIndex]).setString(newSignatureBuffer.toString()); + + // Clear the referenced classes. + // TODO: Properly update the referenced classes. + signatureAttribute.referencedClasses = null; + } + } +} diff --git a/src/proguard/classfile/editor/InterfacesEditor.java b/src/proguard/classfile/editor/InterfacesEditor.java new file mode 100644 index 000000000..8765e36c3 --- /dev/null +++ b/src/proguard/classfile/editor/InterfacesEditor.java @@ -0,0 +1,122 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.util.Arrays; + +/** + * This class can add and delete interfaces to and from classes. References to + * the constant pool must be filled out beforehand. + * + * @author Eric Lafortune + */ +public class InterfacesEditor +{ + private final ProgramClass targetClass; + + + /** + * Creates a new InterfacesEditor that will edit interfaces in the given + * target class. + */ + public InterfacesEditor(ProgramClass targetClass) + { + this.targetClass = targetClass; + } + + + /** + * Adds the specified interface to the target class, if it isn't present yet. + */ + public void addInterface(int interfaceConstantIndex) + { + // Is the interface not yet present? + if (findInterfaceIndex(interfaceConstantIndex) < 0) + { + int interfacesCount = targetClass.u2interfacesCount++; + int[] interfaces = targetClass.u2interfaces; + + // Is the array too small to contain the additional interface? + if (interfaces.length <= interfacesCount) + { + // Create a new array and copy the interfaces into it. + int[] newinterfaces = new int[interfacesCount + 1]; + System.arraycopy(interfaces, 0, newinterfaces, 0, interfacesCount); + interfaces = newinterfaces; + + targetClass.u2interfaces = interfaces; + } + + // Append the interface. + interfaces[interfacesCount] = interfaceConstantIndex; + } + } + + + /** + * Deletes the given interface from the target class, if it is present. + */ + public void deleteInterface(int interfaceConstantIndex) + { + // Is the interface already present? + int interfaceIndex = findInterfaceIndex(interfaceConstantIndex); + if (interfaceIndex >= 0) + { + int interfacesCount = --targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + // Shift the other interfaces in the array. + for (int index = interfaceIndex; index < interfacesCount; index++) + { + interfaces[index] = interfaces[index + 1]; + } + + // Clear the remaining entry in the array. + interfaces[interfacesCount] = 0; + } + } + + + // Small utility methods. + + /** + * Finds the index of the specified interface in the list of interfaces of + * the target class. + */ + private int findInterfaceIndex(int interfaceConstantIndex) + { + int interfacesCount = targetClass.u2interfacesCount; + int[] interfaces = targetClass.u2interfaces; + + for (int index = 0; index < interfacesCount; index++) + { + if (interfaces[index] == interfaceConstantIndex) + { + return index; + } + } + + return -1; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/LineNumberInfoAdder.java b/src/proguard/classfile/editor/LineNumberInfoAdder.java new file mode 100644 index 000000000..e1bd14a4d --- /dev/null +++ b/src/proguard/classfile/editor/LineNumberInfoAdder.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.attribute.visitor.LineNumberInfoVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.*; + +/** + * This LineNumberInfoVisitor adds all line numbers that it visits to the given + * target line number attribute. + */ +public class LineNumberInfoAdder +implements LineNumberInfoVisitor +{ + private final LineNumberTableAttributeEditor lineNumberTableAttributeEditor; + + + /** + * Creates a new LineNumberInfoAdder that will copy line numbers into the + * given target line number table. + */ + public LineNumberInfoAdder(LineNumberTableAttribute targetLineNumberTableAttribute) + { + this.lineNumberTableAttributeEditor = new LineNumberTableAttributeEditor(targetLineNumberTableAttribute); + } + + + // Implementations for LineNumberInfoVisitor. + + public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) + { + // Create a new line number. + LineNumberInfo newLineNumberInfo = + new LineNumberInfo(lineNumberInfo.u2startPC, + lineNumberInfo.u2lineNumber); + + // Add it to the target. + lineNumberTableAttributeEditor.addLineNumberInfo(newLineNumberInfo); + } +} diff --git a/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java b/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java new file mode 100644 index 000000000..f712d4697 --- /dev/null +++ b/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.attribute.*; + +/** + * This class can add line numbers to a given line number table attribute. + * Line numbers to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class LineNumberTableAttributeEditor +{ + private LineNumberTableAttribute targetLineNumberTableAttribute; + + + /** + * Creates a new LineNumberTableAttributeEditor that will edit line numbers + * in the given line number table attribute. + */ + public LineNumberTableAttributeEditor(LineNumberTableAttribute targetLineNumberTableAttribute) + { + this.targetLineNumberTableAttribute = targetLineNumberTableAttribute; + } + + + /** + * Adds a given line number to the line number table attribute. + */ + public void addLineNumberInfo(LineNumberInfo lineNumberInfo) + { + int lineNumberTableLength = targetLineNumberTableAttribute.u2lineNumberTableLength; + LineNumberInfo[] lineNumberTable = targetLineNumberTableAttribute.lineNumberTable; + + // Make sure there is enough space for the new lineNumberInfo. + if (lineNumberTable.length <= lineNumberTableLength) + { + targetLineNumberTableAttribute.lineNumberTable = new LineNumberInfo[lineNumberTableLength+1]; + System.arraycopy(lineNumberTable, 0, + targetLineNumberTableAttribute.lineNumberTable, 0, + lineNumberTableLength); + lineNumberTable = targetLineNumberTableAttribute.lineNumberTable; + } + + // Add the lineNumberInfo. + lineNumberTable[targetLineNumberTableAttribute.u2lineNumberTableLength++] = lineNumberInfo; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/LocalVariableInfoAdder.java b/src/proguard/classfile/editor/LocalVariableInfoAdder.java new file mode 100644 index 000000000..a270fcf8b --- /dev/null +++ b/src/proguard/classfile/editor/LocalVariableInfoAdder.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.attribute.visitor.LocalVariableInfoVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.*; + +/** + * This LocalVariableInfoVisitor adds all line numbers that it visits to the given + * target line number attribute. + */ +public class LocalVariableInfoAdder +implements LocalVariableInfoVisitor +{ + private final ConstantAdder constantAdder; + private final LocalVariableTableAttributeEditor localVariableTableAttributeEditor; + + + /** + * Creates a new LocalVariableInfoAdder that will copy line numbers into the + * given target line number table. + */ + public LocalVariableInfoAdder(ProgramClass targetClass, + LocalVariableTableAttribute targetLocalVariableTableAttribute) + { + this.constantAdder = new ConstantAdder(targetClass); + this.localVariableTableAttributeEditor = new LocalVariableTableAttributeEditor(targetLocalVariableTableAttribute); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Create a new line number. + LocalVariableInfo newLocalVariableInfo = + new LocalVariableInfo(localVariableInfo.u2startPC, + localVariableInfo.u2length, + constantAdder.addConstant(clazz, localVariableInfo.u2nameIndex), + constantAdder.addConstant(clazz, localVariableInfo.u2descriptorIndex), + localVariableInfo.u2index); + + newLocalVariableInfo.referencedClass = localVariableInfo.referencedClass; + + // Add it to the target. + localVariableTableAttributeEditor.addLocalVariableInfo(newLocalVariableInfo); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java b/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java new file mode 100644 index 000000000..b9e601fa1 --- /dev/null +++ b/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.attribute.*; + +/** + * This class can add local variables to a given local variable table attribute. + * Local variables to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class LocalVariableTableAttributeEditor +{ + private LocalVariableTableAttribute targetLocalVariableTableAttribute; + + + /** + * Creates a new LocalVariableTableAttributeEditor that will edit line numbers + * in the given line number table attribute. + */ + public LocalVariableTableAttributeEditor(LocalVariableTableAttribute targetLocalVariableTableAttribute) + { + this.targetLocalVariableTableAttribute = targetLocalVariableTableAttribute; + } + + + /** + * Adds a given line number to the line number table attribute. + */ + public void addLocalVariableInfo(LocalVariableInfo localVariableInfo) + { + int localVariableTableLength = targetLocalVariableTableAttribute.u2localVariableTableLength; + LocalVariableInfo[] localVariableTable = targetLocalVariableTableAttribute.localVariableTable; + + // Make sure there is enough space for the new localVariableInfo. + if (localVariableTable.length <= localVariableTableLength) + { + targetLocalVariableTableAttribute.localVariableTable = new LocalVariableInfo[localVariableTableLength+1]; + System.arraycopy(localVariableTable, 0, + targetLocalVariableTableAttribute.localVariableTable, 0, + localVariableTableLength); + localVariableTable = targetLocalVariableTableAttribute.localVariableTable; + } + + // Add the localVariableInfo. + localVariableTable[targetLocalVariableTableAttribute.u2localVariableTableLength++] = localVariableInfo; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java b/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java new file mode 100644 index 000000000..bed1366a9 --- /dev/null +++ b/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java @@ -0,0 +1,68 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.attribute.visitor.LocalVariableTypeInfoVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.*; + +/** + * This LocalVariableTypeInfoVisitor adds all line numbers that it visits to the given + * target line number attribute. + */ +public class LocalVariableTypeInfoAdder +implements LocalVariableTypeInfoVisitor +{ + private final ConstantAdder constantAdder; + private final LocalVariableTypeTableAttributeEditor localVariableTypeTableAttributeEditor; + + + /** + * Creates a new LocalVariableTypeInfoAdder that will copy line numbers into the + * given target line number table. + */ + public LocalVariableTypeInfoAdder(ProgramClass targetClass, + LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute) + { + this.constantAdder = new ConstantAdder(targetClass); + this.localVariableTypeTableAttributeEditor = new LocalVariableTypeTableAttributeEditor(targetLocalVariableTypeTableAttribute); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Create a new line number. + LocalVariableTypeInfo newLocalVariableTypeInfo = + new LocalVariableTypeInfo(localVariableTypeInfo.u2startPC, + localVariableTypeInfo.u2length, + constantAdder.addConstant(clazz, localVariableTypeInfo.u2nameIndex), + constantAdder.addConstant(clazz, localVariableTypeInfo.u2signatureIndex), + localVariableTypeInfo.u2index); + + // TODO: Clone array. + newLocalVariableTypeInfo.referencedClasses = localVariableTypeInfo.referencedClasses; + + // Add it to the target. + localVariableTypeTableAttributeEditor.addLocalVariableTypeInfo(newLocalVariableTypeInfo); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java b/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java new file mode 100644 index 000000000..00f7ef3de --- /dev/null +++ b/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java @@ -0,0 +1,68 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.attribute.*; + +/** + * This class can add local variables to a given local variable type table + * attribute. + * Local variable types to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class LocalVariableTypeTableAttributeEditor +{ + private LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute; + + + /** + * Creates a new LocalVariableTypeTableAttributeEditor that will edit line numbers + * in the given line number table attribute. + */ + public LocalVariableTypeTableAttributeEditor(LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute) + { + this.targetLocalVariableTypeTableAttribute = targetLocalVariableTypeTableAttribute; + } + + + /** + * Adds a given line number to the line number table attribute. + */ + public void addLocalVariableTypeInfo(LocalVariableTypeInfo localVariableTypeInfo) + { + int localVariableTypeTableLength = targetLocalVariableTypeTableAttribute.u2localVariableTypeTableLength; + LocalVariableTypeInfo[] localVariableTypeTable = targetLocalVariableTypeTableAttribute.localVariableTypeTable; + + // Make sure there is enough space for the new localVariableTypeInfo. + if (localVariableTypeTable.length <= localVariableTypeTableLength) + { + targetLocalVariableTypeTableAttribute.localVariableTypeTable = new LocalVariableTypeInfo[localVariableTypeTableLength+1]; + System.arraycopy(localVariableTypeTable, 0, + targetLocalVariableTypeTableAttribute.localVariableTypeTable, 0, + localVariableTypeTableLength); + localVariableTypeTable = targetLocalVariableTypeTableAttribute.localVariableTypeTable; + } + + // Add the localVariableTypeInfo. + localVariableTypeTable[targetLocalVariableTypeTableAttribute.u2localVariableTypeTableLength++] = localVariableTypeInfo; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/MemberAdder.java b/src/proguard/classfile/editor/MemberAdder.java new file mode 100644 index 000000000..811acae7e --- /dev/null +++ b/src/proguard/classfile/editor/MemberAdder.java @@ -0,0 +1,294 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 MemberVisitor copies all class members that it visits to the given + * target class. Their visitor info is set to the class members from which they + * were copied. + * + * @author Eric Lafortune + */ +public class MemberAdder +extends SimplifiedVisitor +implements MemberVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + + private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; + + + private final ProgramClass targetClass; +// private final boolean addFields; + private final MemberVisitor extraMemberVisitor; + + private final ConstantAdder constantAdder; + private final ClassEditor classEditor; + private final ConstantPoolEditor constantPoolEditor; + + + /** + * Creates a new MemberAdder that will copy methods into the given target + * class. + * @param targetClass the class to which all visited class members will be + * added. + */ + public MemberAdder(ProgramClass targetClass) + { + this(targetClass, null); + } + + + /** + * Creates a new MemberAdder that will copy methods into the given target + * class. + * @param targetClass the class to which all visited class members + * will be added. + * @param extraMemberVisitor an optional member visitor that visits each + * new member right after it has been added. This + * allows changing the visitor info, for instance. + */ +// * @param addFields specifies whether fields should be added, or fused +// * with the present fields. + public MemberAdder(ProgramClass targetClass, +// boolean addFields, + MemberVisitor extraMemberVisitor) + { + this.targetClass = targetClass; +// this.addFields = addFields; + this.extraMemberVisitor = extraMemberVisitor; + + constantAdder = new ConstantAdder(targetClass); + classEditor = new ClassEditor(targetClass); + constantPoolEditor = new ConstantPoolEditor(targetClass); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + //String name = programField.getName(programClass); + //String descriptor = programField.getDescriptor(programClass); + int accessFlags = programField.getAccessFlags(); + + // TODO: Handle field with the same name and descriptor in the target class. + // We currently avoid this case, since renaming the identical field + // still causes confused field references. + //// Does the target class already have such a field? + //ProgramField targetField = (ProgramField)targetClass.findField(name, descriptor); + //if (targetField != null) + //{ + // // Is the field private or static? + // int targetAccessFlags = targetField.getAccessFlags(); + // if ((targetAccessFlags & + // (ClassConstants.INTERNAL_ACC_PRIVATE | + // ClassConstants.INTERNAL_ACC_STATIC)) != 0) + // { + // if (DEBUG) + // { + // System.out.println("MemberAdder: renaming field ["+targetClass+"."+targetField.getName(targetClass)+" "+targetField.getDescriptor(targetClass)+"]"); + // } + // + // // Rename the private or static field. + // targetField.u2nameIndex = + // constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, targetClass.getName())); + // } + // else + // { + // // Keep the non-private and non-static field, but update its + // // contents, in order to keep any references to it valid. + // if (DEBUG) + // { + // System.out.println("MemberAdder: updating field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + // } + // + // // Combine the access flags. + // targetField.u2accessFlags = accessFlags | targetAccessFlags; + // + // // Add and replace any attributes. + // programField.attributesAccept(programClass, + // new AttributeAdder(targetClass, + // targetField, + // true)); + // + // // Don't add a new field. + // return; + // } + //} + + if (DEBUG) + { + System.out.println("MemberAdder: copying field ["+programClass+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + } + + // Create a copy of the field. + ProgramField newProgramField = + new ProgramField(accessFlags, + constantAdder.addConstant(programClass, programField.u2nameIndex), + constantAdder.addConstant(programClass, programField.u2descriptorIndex), + 0, + programField.u2attributesCount > 0 ? + new Attribute[programField.u2attributesCount] : + EMPTY_ATTRIBUTES, + programField.referencedClass); + + // Link to its visitor info. + newProgramField.setVisitorInfo(programField); + + // Copy its attributes. + programField.attributesAccept(programClass, + new AttributeAdder(targetClass, + newProgramField, + false)); + + // Add the completed field. + classEditor.addField(newProgramField); + + // Visit the newly added field, if necessary. + if (extraMemberVisitor != null) + { + extraMemberVisitor.visitProgramField(targetClass, newProgramField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + String name = programMethod.getName(programClass); + String descriptor = programMethod.getDescriptor(programClass); + int accessFlags = programMethod.getAccessFlags(); + + // Does the target class already have such a method? + ProgramMethod targetMethod = (ProgramMethod)targetClass.findMethod(name, descriptor); + if (targetMethod != null) + { + // is this source method abstract? + if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + // Keep the target method. + if (DEBUG) + { + System.out.println("MemberAdder: skipping abstract method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + } + + // Don't add a new method. + return; + } + + // Is the target method abstract? + int targetAccessFlags = targetMethod.getAccessFlags(); + if ((targetAccessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + // Keep the abstract method, but update its contents, in order + // to keep any references to it valid. + if (DEBUG) + { + System.out.println("MemberAdder: updating method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + } + + // Replace the access flags. + targetMethod.u2accessFlags = + accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL; + + // Add and replace the attributes. + programMethod.attributesAccept(programClass, + new AttributeAdder(targetClass, + targetMethod, + true)); + + // Don't add a new method. + return; + } + + if (DEBUG) + { + System.out.println("MemberAdder: renaming method ["+targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); + } + + // TODO: Handle non-abstract method with the same name and descriptor in the target class. + // We currently avoid this case, since renaming the identical method + // still causes confused method references. + //// Rename the private (non-abstract) or static method. + //targetMethod.u2nameIndex = + // constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, descriptor)); + } + + if (DEBUG) + { + System.out.println("MemberAdder: copying method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + } + + // Create a copy of the method. + ProgramMethod newProgramMethod = + new ProgramMethod(accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL, + constantAdder.addConstant(programClass, programMethod.u2nameIndex), + constantAdder.addConstant(programClass, programMethod.u2descriptorIndex), + 0, + programMethod.u2attributesCount > 0 ? + new Attribute[programMethod.u2attributesCount] : + EMPTY_ATTRIBUTES, + programMethod.referencedClasses != null ? + (Clazz[])programMethod.referencedClasses.clone() : + null); + + // Link to its visitor info. + newProgramMethod.setVisitorInfo(programMethod); + + // Copy its attributes. + programMethod.attributesAccept(programClass, + new AttributeAdder(targetClass, + newProgramMethod, + false)); + + // Add the completed method. + classEditor.addMethod(newProgramMethod); + + // Visit the newly added method, if necessary. + if (extraMemberVisitor != null) + { + extraMemberVisitor.visitProgramMethod(targetClass, newProgramMethod); + } + } + + + // Small utility methods. + + /** + * Returns a unique class member name, based on the given name and descriptor. + */ + private String newUniqueMemberName(String name, String descriptor) + { + return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? + ClassConstants.INTERNAL_METHOD_NAME_INIT : + name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); + } +} diff --git a/src/proguard/classfile/editor/MemberReferenceFixer.java b/src/proguard/classfile/editor/MemberReferenceFixer.java new file mode 100644 index 000000000..b62304789 --- /dev/null +++ b/src/proguard/classfile/editor/MemberReferenceFixer.java @@ -0,0 +1,447 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor fixes constant pool field and method references to fields + * and methods whose names or descriptors have changed. + * + * @author Eric Lafortune + */ +public class MemberReferenceFixer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private static final boolean DEBUG = false; + + + private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); + + // Parameter for the visitor methods. + private int constantIndex; + + // Return values for the visitor methods. + private boolean isInterfaceMethod; + private boolean stackSizesMayHaveChanged; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + stackSizesMayHaveChanged = false; + + // Fix the constant pool entries. + for (int index = 1; index < programClass.u2constantPoolCount; index++) + { + Constant constant = programClass.constantPool[index]; + if (constant != null) + { + // Fix the entry, replacing it entirely if needed. + this.constantIndex = index; + + constant.accept(programClass, this); + } + } + + // Fix the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Fix the attributes. + programClass.attributesAccept(this); + } + + + // 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); + + if (!stringConstant.getString(clazz).equals(newName)) + { + if (DEBUG) + { + debug(clazz, stringConstant, referencedClass, referencedMember); + } + + // Update the name. + stringConstant.u2stringIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newName); + } + } + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + // Do we know the referenced field? + Member referencedMember = fieldrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = fieldrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!fieldrefConstant.getName(clazz).equals(newName) || + !fieldrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, fieldrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + fieldrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + } + } + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + // Do we know the referenced interface method? + Member referencedMember = interfaceMethodrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = interfaceMethodrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!interfaceMethodrefConstant.getName(clazz).equals(newName) || + !interfaceMethodrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, interfaceMethodrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + interfaceMethodrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + + // Remember that the stack sizes of the methods in this class + // may have changed. + stackSizesMayHaveChanged = true; + } + + // Check if this is an interface method. + isInterfaceMethod = true; + clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this); + + // Has the method become a non-interface method? + if (!isInterfaceMethod) + { + if (DEBUG) + { + System.out.println("MemberReferenceFixer:"); + 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. + ((ProgramClass)clazz).constantPool[this.constantIndex] = + new MethodrefConstant(interfaceMethodrefConstant.u2classIndex, + interfaceMethodrefConstant.u2nameAndTypeIndex, + referencedClass, + referencedMember); + } + } + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + // Do we know the referenced method? + Member referencedMember = methodrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = methodrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!methodrefConstant.getName(clazz).equals(newName) || + !methodrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, methodrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + methodrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + + // Remember that the stack sizes of the methods in this class + // may have changed. + stackSizesMayHaveChanged = true; + } + + // Check if this is an interface method. + isInterfaceMethod = false; + clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this); + + // Has the method become an interface method? + if (isInterfaceMethod) + { + if (DEBUG) + { + System.out.println("MemberReferenceFixer:"); + 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. + ((ProgramClass)clazz).constantPool[this.constantIndex] = + new InterfaceMethodrefConstant(methodrefConstant.u2classIndex, + methodrefConstant.u2nameAndTypeIndex, + referencedClass, + referencedMember); + } + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Check if this class entry is an array type. + if (ClassUtil.isInternalArrayType(classConstant.getName(clazz))) + { + isInterfaceMethod = false; + } + else + { + // Check if this class entry refers to an interface class. + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass != null) + { + isInterfaceMethod = (referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0; + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Fix the attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + Member referencedMember = enclosingMethodAttribute.referencedMethod; + if (referencedMember != null) + { + Clazz referencedClass = enclosingMethodAttribute.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!enclosingMethodAttribute.getName(clazz).equals(newName) || + !enclosingMethodAttribute.getType(clazz).equals(newType)) + { + // Update the name and type index. + enclosingMethodAttribute.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, + newType); + } + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Recompute the maximum stack size if necessary. + if (stackSizesMayHaveChanged) + { + stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + } + + // Fix the nested attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + 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 AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Fix the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + fixElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + fixElementValue(clazz, annotation, enumConstantElementValue); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + fixElementValue(clazz, annotation, classElementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + fixElementValue(clazz, annotation, annotationElementValue); + + // Fix the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + fixElementValue(clazz, annotation, arrayElementValue); + + // Fix the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Fixes the method reference of the element value, if any. + */ + private void fixElementValue(Clazz clazz, + Annotation annotation, + ElementValue elementValue) + { + // Do we know the referenced method? + Member referencedMember = elementValue.referencedMethod; + if (referencedMember != null) + { + // Does it have a new name or type? + String methodName = elementValue.getMethodName(clazz); + String newMethodName = referencedMember.getName(elementValue.referencedClass); + + if (!methodName.equals(newMethodName)) + { + // Update the element name index. + elementValue.u2elementNameIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newMethodName); + } + } + } + + + private void debug(Clazz clazz, + StringConstant stringConstant, + Clazz referencedClass, + Member referencedMember) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" ["+clazz.getName()+"]: String ["+ + stringConstant.getString(clazz)+"] -> ["+ + referencedClass.getName()+"."+referencedMember.getName(referencedClass)+" "+referencedMember.getDescriptor(referencedClass)+"]"); + } + + + private void debug(Clazz clazz, + RefConstant refConstant, + Clazz referencedClass, + Member referencedMember) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" ["+clazz.getName()+"]: ["+ + refConstant.getClassName(clazz)+"."+refConstant.getName(clazz)+" "+refConstant.getType(clazz)+"] -> ["+ + referencedClass.getName()+"."+referencedMember.getName(referencedClass)+" "+referencedMember.getDescriptor(referencedClass)+"]"); + } +} diff --git a/src/proguard/classfile/editor/MethodInvocationFixer.java b/src/proguard/classfile/editor/MethodInvocationFixer.java new file mode 100644 index 000000000..e457e6328 --- /dev/null +++ b/src/proguard/classfile/editor/MethodInvocationFixer.java @@ -0,0 +1,243 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; + +/** + * This AttributeVisitor fixes all inappropriate special/virtual/static/interface + * invocations of the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class MethodInvocationFixer +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor +{ + private static final boolean DEBUG = false; + + + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + // Return values for the visitor methods. + 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) + { + // 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 visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + int constantIndex = constantInstruction.constantIndex; + + // Get information on the called class and method, if present. + referencedMethod = null; + + clazz.constantPoolEntryAccept(constantIndex, this); + + // Did we find the called class and method? + if (referencedClass != null && + referencedMethod != null) + { + // Do we need to update the opcode? + byte opcode = constantInstruction.opcode; + + // Is the method static? + 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 ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, + constantIndex); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // Is the method private, or an instance initializer? + 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 ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, + constantIndex); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // Is the method an interface method? + else if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + int invokeinterfaceConstant = + (ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false)) << 8; + + // But is it not an interface invocation, or is the parameter + // size incorrect? + if (opcode != InstructionConstants.OP_INVOKEINTERFACE || + constantInstruction.constant != invokeinterfaceConstant) + { + // Fix the parameter size of the interface invocation. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKEINTERFACE, + constantIndex, + invokeinterfaceConstant); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + + // The method is not static, private, an instance initializer, or + // an interface method. + else + { + // But is it not a virtual invocation (or a special invocation, + // but not a super call)? + if (opcode != InstructionConstants.OP_INVOKEVIRTUAL && + (opcode != InstructionConstants.OP_INVOKESPECIAL || + clazz.equals(referencedClass) || + !clazz.extends_(referencedClass))) + { + // Replace the invocation by an invokevirtual instruction. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, + constantIndex); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + if (DEBUG) + { + debug(clazz, method, offset, constantInstruction, replacementInstruction); + } + } + } + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + // Remember the referenced class. Note that we're interested in the + // class of the method reference, not in the class in which the + // method was actually found, unless it is an array type. + // + if (ClassUtil.isInternalArrayType(refConstant.getClassName(clazz))) + { + // For an array type, the class will be java.lang.Object. + referencedClass = refConstant.referencedClass; + } + else + { + clazz.constantPoolEntryAccept(refConstant.u2classIndex, this); + } + + // Remember the referenced method. + referencedMethodClass = refConstant.referencedClass; + referencedMethod = refConstant.referencedMember; + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Remember the referenced class. + referencedClass = classConstant.referencedClass; + } + + + // Small utility methods. + + private void debug(Clazz clazz, + Method method, + int offset, + ConstantInstruction constantInstruction, + Instruction replacementInstruction) + { + System.out.println("MethodInvocationFixer:"); + 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 = "+(ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false))); + } + System.out.println(" Replacement instruction = "+replacementInstruction.toString(offset)); + } +} diff --git a/src/proguard/classfile/editor/NameAndTypeShrinker.java b/src/proguard/classfile/editor/NameAndTypeShrinker.java new file mode 100644 index 000000000..650f9baf8 --- /dev/null +++ b/src/proguard/classfile/editor/NameAndTypeShrinker.java @@ -0,0 +1,188 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.ConstantPoolRemapper; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.Arrays; + + +/** + * This ClassVisitor removes NameAndType constant pool entries that are not + * used. + * + * @author Eric Lafortune + */ +public class NameAndTypeShrinker +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(); + + private int[] constantIndexMap; + private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Mark the NameAndType entries referenced by all other constant pool + // entries. + programClass.constantPoolEntriesAccept(this); + + // Mark the NameAndType entries referenced by all EnclosingMethod + // attributes. + programClass.attributesAccept(this); + + // Shift the used constant pool entries together, filling out the + // index map. + int newConstantPoolCount = + shrinkConstantPool(programClass.constantPool, + programClass.u2constantPoolCount); + + // Remap the references to the constant pool if it has shrunk. + if (newConstantPoolCount < programClass.u2constantPoolCount) + { + programClass.u2constantPoolCount = newConstantPoolCount; + + // Remap all constant pool references. + constantPoolRemapper.setConstantIndexMap(constantIndexMap); + constantPoolRemapper.visitProgramClass(programClass); + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + markNameAndTypeConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + markNameAndTypeConstant(clazz, refConstant.u2nameAndTypeIndex); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) + { + markNameAndTypeConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex); + } + } + + + // Small utility methods. + + /** + * Marks the given UTF-8 constant pool entry of the given class. + */ + private void markNameAndTypeConstant(Clazz clazz, int index) + { + markAsUsed((NameAndTypeConstant)((ProgramClass)clazz).getConstant(index)); + } + + + /** + * Marks the given VisitorAccepter as being used. + * In this context, the VisitorAccepter will be a NameAndTypeConstant object. + */ + private void markAsUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(USED); + } + + + /** + * Returns whether the given VisitorAccepter has been marked as being used. + * In this context, the VisitorAccepter will be a NameAndTypeConstant object. + */ + private boolean isUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == USED; + } + + + /** + * Removes all NameAndType 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) + { + // Create a new index map, if necessary. + if (constantIndexMap == null || + 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 = constant.getTag() != ClassConstants.CONSTANT_NameAndType || + isUsed(constant); + } + + if (isUsed) + { + constantPool[counter++] = constant; + } + } + + // Clear the remaining constant pool elements. + Arrays.fill(constantPool, counter, length, null); + + return counter; + } +} diff --git a/src/proguard/classfile/editor/NamedAttributeDeleter.java b/src/proguard/classfile/editor/NamedAttributeDeleter.java new file mode 100644 index 000000000..6aa5cdf64 --- /dev/null +++ b/src/proguard/classfile/editor/NamedAttributeDeleter.java @@ -0,0 +1,54 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.*; +import proguard.util.StringMatcher; + + +/** + * This ClassVisitor deletes attributes with a given name in the program + * classes that it visits. + * + * @author Eric Lafortune + */ +public class NamedAttributeDeleter implements ClassVisitor +{ + private final String attributeName; + + + public NamedAttributeDeleter(String attributeName) + { + this.attributeName = attributeName; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + new AttributesEditor(programClass, false).deleteAttribute(attributeName); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java b/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java new file mode 100644 index 000000000..232747fd5 --- /dev/null +++ b/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.attribute.annotation.*; + +/** + * This class can add annotations to a given parameter annotations attribute. + * Annotations to be added must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class ParameterAnnotationsAttributeEditor +{ + private ParameterAnnotationsAttribute targetParameterAnnotationsAttribute; + + + /** + * Creates a new ParameterAnnotationsAttributeEditor that will edit + * annotations in the given parameter annotations attribute. + */ + public ParameterAnnotationsAttributeEditor(ParameterAnnotationsAttribute targetParameterAnnotationsAttribute) + { + this.targetParameterAnnotationsAttribute = targetParameterAnnotationsAttribute; + } + + + /** + * Adds a given annotation to the annotations attribute. + */ + public void addAnnotation(int parameterIndex, Annotation annotation) + { + int annotationsCount = targetParameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]; + Annotation[] annotations = targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex]; + + // Make sure there is enough space for the new annotation. + if (annotations == null || + annotations.length <= annotationsCount) + { + targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex] = new Annotation[annotationsCount+1]; + if (annotations != null) + { + System.arraycopy(annotations, 0, + targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex], 0, + annotationsCount); + } + annotations = targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex]; + } + + // Add the annotation. + annotations[targetParameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]++] = annotation; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/StackSizeUpdater.java b/src/proguard/classfile/editor/StackSizeUpdater.java new file mode 100644 index 000000000..d90b3d08d --- /dev/null +++ b/src/proguard/classfile/editor/StackSizeUpdater.java @@ -0,0 +1,54 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor computes and updates the maximum stack size of the + * code attributes that it visits. + * + * @author Eric Lafortune + */ +public class StackSizeUpdater +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final StackSizeComputer stackSizeComputer = new StackSizeComputer(); + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Compute the stack sizes. + stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute); + + // Update the maximum stack size. + codeAttribute.u2maxStack = stackSizeComputer.getMaxStackSize(); + } +} diff --git a/src/proguard/classfile/editor/SubclassAdder.java b/src/proguard/classfile/editor/SubclassAdder.java new file mode 100644 index 000000000..717bb1c94 --- /dev/null +++ b/src/proguard/classfile/editor/SubclassAdder.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * This ClassVisitor adds the given class to the list of subclasses of the + * classes that it visits. + * + * @author Eric Lafortune + */ +public class SubclassAdder +implements ClassVisitor +{ + private final Clazz subclass; + + + /** + * Creates a new SubclassAdder that will add the given subclass. + */ + public SubclassAdder(Clazz subclass) + { + this.subclass = subclass; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.addSubClass(subclass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.addSubClass(subclass); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/SubclassToAdder.java b/src/proguard/classfile/editor/SubclassToAdder.java new file mode 100644 index 000000000..109152b7b --- /dev/null +++ b/src/proguard/classfile/editor/SubclassToAdder.java @@ -0,0 +1,60 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * This ClassVisitor adds all classes that it visits to the list of subclasses + * of the given target class. + * + * @author Eric Lafortune + */ +public class SubclassToAdder +implements ClassVisitor +{ + private final Clazz targetClass; + + + /** + * Creates a new SubclassAdder that will add subclasses to the given + * target class. + */ + public SubclassToAdder(Clazz targetClass) + { + this.targetClass = targetClass; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + targetClass.addSubClass(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + targetClass.addSubClass(libraryClass); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/Utf8Shrinker.java b/src/proguard/classfile/editor/Utf8Shrinker.java new file mode 100644 index 000000000..eda582697 --- /dev/null +++ b/src/proguard/classfile/editor/Utf8Shrinker.java @@ -0,0 +1,455 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.preverification.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.ConstantPoolRemapper; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +import java.util.Arrays; + + +/** + * This ClassVisitor removes UTF-8 constant pool entries that are not used. + * + * @author Eric Lafortune + */ +public class Utf8Shrinker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor, + AttributeVisitor, + InnerClassesInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + // A visitor info flag to indicate the UTF-8 constant pool entry is being used. + private static final Object USED = new Object(); + + private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; + private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Mark the UTF-8 entries referenced by the other constant pool entries. + programClass.constantPoolEntriesAccept(this); + + // Mark the UTF-8 entries referenced by the fields and methods. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Mark the UTF-8 entries referenced by the attributes. + programClass.attributesAccept(this); + + // Shift the used constant pool entries together, filling out the + // index map. + int newConstantPoolCount = + shrinkConstantPool(programClass.constantPool, + programClass.u2constantPoolCount); + + // Remap the references to the constant pool if it has shrunk. + if (newConstantPoolCount < programClass.u2constantPoolCount) + { + programClass.u2constantPoolCount = newConstantPoolCount; + + // Remap all constant pool references. + constantPoolRemapper.setConstantIndexMap(constantIndexMap); + constantPoolRemapper.visitProgramClass(programClass); + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Mark the name and descriptor UTF-8 entries. + markCpUtf8Entry(programClass, programMember.u2nameIndex); + markCpUtf8Entry(programClass, programMember.u2descriptorIndex); + + // Mark the UTF-8 entries referenced by the attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + markCpUtf8Entry(clazz, stringConstant.u2stringIndex); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + markCpUtf8Entry(clazz, classConstant.u2nameIndex); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + markCpUtf8Entry(clazz, nameAndTypeConstant.u2nameIndex); + markCpUtf8Entry(clazz, nameAndTypeConstant.u2descriptorIndex); + } + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + // This is the best we can do for unknown attributes. + markCpUtf8Entry(clazz, unknownAttribute.u2attributeNameIndex); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + markCpUtf8Entry(clazz, sourceFileAttribute.u2attributeNameIndex); + + markCpUtf8Entry(clazz, sourceFileAttribute.u2sourceFileIndex); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + markCpUtf8Entry(clazz, sourceDirAttribute.u2attributeNameIndex); + + markCpUtf8Entry(clazz, sourceDirAttribute.u2sourceDirIndex); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + markCpUtf8Entry(clazz, innerClassesAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the inner classes. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + 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 visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + markCpUtf8Entry(clazz, deprecatedAttribute.u2attributeNameIndex); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + markCpUtf8Entry(clazz, syntheticAttribute.u2attributeNameIndex); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + markCpUtf8Entry(clazz, signatureAttribute.u2attributeNameIndex); + + markCpUtf8Entry(clazz, signatureAttribute.u2signatureIndex); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + markCpUtf8Entry(clazz, constantValueAttribute.u2attributeNameIndex); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + markCpUtf8Entry(clazz, exceptionsAttribute.u2attributeNameIndex); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + markCpUtf8Entry(clazz, codeAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + markCpUtf8Entry(clazz, stackMapAttribute.u2attributeNameIndex); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + markCpUtf8Entry(clazz, stackMapTableAttribute.u2attributeNameIndex); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + markCpUtf8Entry(clazz, lineNumberTableAttribute.u2attributeNameIndex); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + markCpUtf8Entry(clazz, localVariableTableAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + markCpUtf8Entry(clazz, localVariableTypeTableAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the local variable types. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + markCpUtf8Entry(clazz, annotationsAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + markCpUtf8Entry(clazz, parameterAnnotationsAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + markCpUtf8Entry(clazz, annotationDefaultAttribute.u2attributeNameIndex); + + // Mark the UTF-8 entries referenced by the element value. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + if (innerClassesInfo.u2innerNameIndex != 0) + { + markCpUtf8Entry(clazz, innerClassesInfo.u2innerNameIndex); + } + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + markCpUtf8Entry(clazz, localVariableInfo.u2nameIndex); + markCpUtf8Entry(clazz, localVariableInfo.u2descriptorIndex); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + markCpUtf8Entry(clazz, localVariableTypeInfo.u2nameIndex); + markCpUtf8Entry(clazz, localVariableTypeInfo.u2signatureIndex); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + markCpUtf8Entry(clazz, annotation.u2typeIndex); + + // Mark the UTF-8 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) + { + 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(clazz, constantElementValue.u2constantValueIndex); + } + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + if (enumConstantElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, enumConstantElementValue.u2elementNameIndex); + } + + markCpUtf8Entry(clazz, enumConstantElementValue.u2typeNameIndex); + markCpUtf8Entry(clazz, enumConstantElementValue.u2constantNameIndex); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + if (classElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, classElementValue.u2elementNameIndex); + } + + markCpUtf8Entry(clazz, classElementValue.u2classInfoIndex); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + if (annotationElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, annotationElementValue.u2elementNameIndex); + } + + // Mark the UTF-8 entries referenced by the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + if (arrayElementValue.u2elementNameIndex != 0) + { + markCpUtf8Entry(clazz, arrayElementValue.u2elementNameIndex); + } + + // Mark the UTF-8 entries referenced by the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Marks the given UTF-8 constant pool entry of the given class. + */ + private void markCpUtf8Entry(Clazz clazz, int index) + { + markAsUsed((Utf8Constant)((ProgramClass)clazz).getConstant(index)); + } + + + /** + * Marks the given VisitorAccepter as being used. + * In this context, the VisitorAccepter will be a Utf8Constant object. + */ + private void markAsUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(USED); + } + + + /** + * Returns whether the given VisitorAccepter has been marked as being used. + * In this context, the VisitorAccepter will be a Utf8Constant object. + */ + private boolean isUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == USED; + } + + + /** + * Removes all UTF-8 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) + { + // Create a new index map, if necessary. + 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 = constant.getTag() != ClassConstants.CONSTANT_Utf8 || + isUsed(constant); + } + + if (isUsed) + { + constantPool[counter++] = constant; + } + } + + // Clear the remaining constant pool elements. + Arrays.fill(constantPool, counter, length, null); + + return counter; + } +} diff --git a/src/proguard/classfile/editor/VariableCleaner.java b/src/proguard/classfile/editor/VariableCleaner.java new file mode 100644 index 000000000..63b7d4a91 --- /dev/null +++ b/src/proguard/classfile/editor/VariableCleaner.java @@ -0,0 +1,271 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import java.util.Arrays; + +/** + * This AttributeVisitor cleans up variable tables in all code attributes that + * it visits. It trims overlapping local variables. It removes empty local + * variables and empty local variable tables. + * + * @author Eric Lafortune + */ +public class VariableCleaner +extends SimplifiedVisitor +implements AttributeVisitor +{ + private boolean deleteLocalVariableTableAttribute; + private boolean deleteLocalVariableTypeTableAttribute; + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + deleteLocalVariableTableAttribute = false; + deleteLocalVariableTypeTableAttribute = false; + + // Trim the local variable table and the local variable type table. + codeAttribute.attributesAccept(clazz, method, this); + + // Delete the local variable table if it ended up empty. + if (deleteLocalVariableTableAttribute) + { + AttributesEditor editor = + new AttributesEditor((ProgramClass)clazz, + (ProgramMember)method, + codeAttribute, + true); + + editor.deleteAttribute(ClassConstants.ATTR_LocalVariableTable); + } + + // Delete the local variable type table if it ended up empty. + if (deleteLocalVariableTypeTableAttribute) + { + AttributesEditor editor = + new AttributesEditor((ProgramClass)clazz, + (ProgramMember)method, + codeAttribute, + true); + + editor.deleteAttribute(ClassConstants.ATTR_LocalVariableTypeTable); + } + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Clean up local variables that aren't used. + localVariableTableAttribute.u2localVariableTableLength = + removeUnusedLocalVariables(localVariableTableAttribute.localVariableTable, + localVariableTableAttribute.u2localVariableTableLength, + codeAttribute.u2maxLocals); + + // Trim the code blocks of the local variables. + trimLocalVariables(localVariableTableAttribute.localVariableTable, + localVariableTableAttribute.u2localVariableTableLength, + codeAttribute.u2maxLocals); + + // Delete the attribute in a moment, if it is empty. + if (localVariableTableAttribute.u2localVariableTableLength == 0) + { + deleteLocalVariableTableAttribute = true; + } + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Clean up local variables that aren't used. + localVariableTypeTableAttribute.u2localVariableTypeTableLength = + removeUnusedLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, + localVariableTypeTableAttribute.u2localVariableTypeTableLength, + codeAttribute.u2maxLocals); + + // Trim the code blocks of the local variables. + trimLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, + localVariableTypeTableAttribute.u2localVariableTypeTableLength, + codeAttribute.u2maxLocals); + + // Delete the attribute in a moment, if it is empty. + if (localVariableTypeTableAttribute.u2localVariableTypeTableLength == 0) + { + deleteLocalVariableTypeTableAttribute = true; + } + } + + + // Small utility methods. + + /** + * Returns the given list of local variables, without the ones that aren't + * used. + */ + private int removeUnusedLocalVariables(LocalVariableInfo[] localVariableInfos, + int localVariableInfoCount, + int maxLocals) + { + // Overwrite all empty local variable entries. + // Do keep parameter entries. + int newIndex = 0; + for (int index = 0; index < localVariableInfoCount; index++) + { + LocalVariableInfo localVariableInfo = localVariableInfos[index]; + + if (localVariableInfo.u2index >= 0 && + localVariableInfo.u2index < maxLocals && + (localVariableInfo.u2startPC == 0 || + localVariableInfo.u2length > 0)) + { + localVariableInfos[newIndex++] = localVariableInfos[index]; + } + } + + // Clean up any remaining array elements. + Arrays.fill(localVariableInfos, newIndex, localVariableInfoCount, null); + + return newIndex; + } + + + /** + * Returns the given list of local variable types, without the ones that + * aren't used. + */ + private int removeUnusedLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, + int localVariableTypeInfoCount, + int maxLocals) + { + // Overwrite all empty local variable type entries. + // Do keep parameter entries. + int newIndex = 0; + for (int index = 0; index < localVariableTypeInfoCount; index++) + { + LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; + + if (localVariableTypeInfo.u2index >= 0 && + localVariableTypeInfo.u2index < maxLocals && + (localVariableTypeInfo.u2startPC == 0 || + localVariableTypeInfo.u2length > 0)) + { + localVariableTypeInfos[newIndex++] = localVariableTypeInfos[index]; + } + } + + // Clean up any remaining array elements. + Arrays.fill(localVariableTypeInfos, newIndex, localVariableTypeInfoCount, null); + + return newIndex; + } + + + /** + * Sorts the given list of local variables and trims their code blocks + * when necessary. The block is trimmed at the end, which is a bit + * arbitrary. + */ + private void trimLocalVariables(LocalVariableInfo[] localVariableInfos, + int localVariableInfoCount, + int maxLocals) + { + // Sort the local variable entries. + Arrays.sort(localVariableInfos, 0, localVariableInfoCount); + + int[] startPCs = createMaxArray(maxLocals); + + // Trim the local variable entries, starting at the last one. + for (int index = localVariableInfoCount-1; index >= 0; index--) + { + LocalVariableInfo localVariableInfo = localVariableInfos[index]; + + // Make sure the variable's code block doesn't overlap with the + // next one for the same variable. + int maxLength = startPCs[localVariableInfo.u2index] - + localVariableInfo.u2startPC; + + if (localVariableInfo.u2length > maxLength) + { + localVariableInfo.u2length = maxLength; + } + + startPCs[localVariableInfo.u2index] = localVariableInfo.u2startPC; + } + } + + + /** + * Sorts the given list of local variable types and trims their code blocks + * when necessary. The block is trimmed at the end, which is a bit + * arbitrary. + */ + private void trimLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, + int localVariableTypeInfoCount, + int maxLocals) + { + // Sort the local variable entries. + Arrays.sort(localVariableTypeInfos, 0, localVariableTypeInfoCount); + + int[] startPCs = createMaxArray(maxLocals); + + // Trim the local variable entries, starting at the last one. + for (int index = localVariableTypeInfoCount-1; index >= 0; index--) + { + LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; + + // Make sure the variable's code block doesn't overlap with the + // next one for the same variable. + int maxLength = startPCs[localVariableTypeInfo.u2index] - + localVariableTypeInfo.u2startPC; + + if (localVariableTypeInfo.u2length > maxLength) + { + localVariableTypeInfo.u2length = maxLength; + } + + startPCs[localVariableTypeInfo.u2index] = localVariableTypeInfo.u2startPC; + } + } + + + /** + * Creates an integer array of the given length, initialized with + * Integer.MAX_VALUE. + */ + private int[] createMaxArray(int length) + { + int[] startPCs = new int[length]; + for (int index = 0; index < length; index++) + { + startPCs[index] = Integer.MAX_VALUE; + } + return startPCs; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/VariableEditor.java b/src/proguard/classfile/editor/VariableEditor.java new file mode 100644 index 000000000..b5143b56d --- /dev/null +++ b/src/proguard/classfile/editor/VariableEditor.java @@ -0,0 +1,130 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import java.util.Arrays; + +/** + * This AttributeVisitor accumulates specified changes to local variables, and + * then applies these accumulated changes to the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class VariableEditor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private boolean modified; + + private boolean[] deleted = new boolean[ClassConstants.TYPICAL_VARIABLES_SIZE]; + private int[] variableMap = new int[ClassConstants.TYPICAL_VARIABLES_SIZE]; + + private final VariableRemapper variableRemapper = new VariableRemapper(); + + + /** + * Resets the accumulated code changes. + * @param maxLocals the length of the local variable frame that will be + * edited next. + */ + public void reset(int maxLocals) + { + // Try to reuse the previous array. + if (deleted.length < maxLocals) + { + // Create a new array. + deleted = new boolean[maxLocals]; + } + else + { + // Reset the array. + Arrays.fill(deleted, 0, maxLocals, false); + } + + modified = false; + } + + + /** + * Remembers to delete the given variable. + * @param variableIndex the index of the variable to be deleted. + */ + public void deleteVariable(int variableIndex) + { + deleted[variableIndex] = true; + + modified = true; + } + + + /** + * Returns whether the given variable at the given offset has deleted. + */ + public boolean isDeleted(int instructionOffset) + { + return deleted[instructionOffset]; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Avoid doing any work if nothing is changing anyway. + if (!modified) + { + return; + } + + int oldMaxLocals = codeAttribute.u2maxLocals; + + // Make sure there is a sufficiently large variable map. + if (variableMap.length < oldMaxLocals) + { + variableMap = new int[oldMaxLocals]; + } + + // Fill out the variable map. + int newVariableIndex = 0; + for (int oldVariableIndex = 0; oldVariableIndex < oldMaxLocals; oldVariableIndex++) + { + variableMap[oldVariableIndex] = deleted[oldVariableIndex] ? + -1 : newVariableIndex++; + } + + // Set the map. + variableRemapper.setVariableMap(variableMap); + + // Remap the variables. + variableRemapper.visitCodeAttribute(clazz, method, codeAttribute); + + // Update the length of local variable frame. + codeAttribute.u2maxLocals = newVariableIndex; + } +} diff --git a/src/proguard/classfile/editor/VariableRemapper.java b/src/proguard/classfile/editor/VariableRemapper.java new file mode 100644 index 000000000..ca9d88ab0 --- /dev/null +++ b/src/proguard/classfile/editor/VariableRemapper.java @@ -0,0 +1,156 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor remaps variable indexes in all attributes that it + * visits, based on a given index map. + * + * @author Eric Lafortune + */ +public class VariableRemapper +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor +{ + private static final boolean DEBUG = false; + + + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + private int[] variableMap; + + + /** + * Sets the given mapping of old variable indexes to their new indexes. + * Variables that should disappear can be mapped to -1. + */ + public void setVariableMap(int[] variableMap) + { + this.variableMap = variableMap; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println("VariableRemapper: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + for (int index= 0; index < codeAttribute.u2maxLocals; index++) + { + System.out.println(" v"+index+" -> "+variableMap[index]); + } + } + + // Remap the variables of the attributes, before editing the code and + // cleaning up its local variable frame. + codeAttribute.attributesAccept(clazz, method, this); + + // Initially, the code attribute editor doesn't contain any changes. + 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); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Remap the variable references of the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Remap the variable references of the local variables. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + localVariableInfo.u2index = + remapVariable(localVariableInfo.u2index); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + localVariableTypeInfo.u2index = + remapVariable(localVariableTypeInfo.u2index); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + // Is the new variable index different from the original one? + int oldVariableIndex = variableInstruction.variableIndex; + int newVariableIndex = remapVariable(oldVariableIndex); + if (newVariableIndex != oldVariableIndex) + { + // Replace the instruction. + Instruction replacementInstruction = + new VariableInstruction(variableInstruction.opcode, + newVariableIndex, + variableInstruction.constant); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + } + } + + + // Small utility methods. + + /** + * Returns the new variable index of the given variable. + */ + private int remapVariable(int variableIndex) + { + return variableMap[variableIndex]; + } +} diff --git a/src/proguard/classfile/editor/VariableSizeUpdater.java b/src/proguard/classfile/editor/VariableSizeUpdater.java new file mode 100644 index 000000000..2feaa9dac --- /dev/null +++ b/src/proguard/classfile/editor/VariableSizeUpdater.java @@ -0,0 +1,105 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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. It also cleans up the local + * variable tables. + * + * @author Eric Lafortune + */ +public class VariableSizeUpdater +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + + private VariableCleaner variableCleaner = new VariableCleaner(); + + + // 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), + method.getAccessFlags()); + + 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); + + // Remove the unused variables of the attributes. + variableCleaner.visitCodeAttribute(clazz, method, codeAttribute); + } + + + // 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/editor/package.html b/src/proguard/classfile/editor/package.html new file mode 100644 index 000000000..d37f541f7 --- /dev/null +++ b/src/proguard/classfile/editor/package.html @@ -0,0 +1,3 @@ + +This package contains visitors to edit byte code. + diff --git a/src/proguard/classfile/instruction/BranchInstruction.java b/src/proguard/classfile/instruction/BranchInstruction.java new file mode 100644 index 000000000..f3a708057 --- /dev/null +++ b/src/proguard/classfile/instruction/BranchInstruction.java @@ -0,0 +1,179 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This interface describes an instruction that branches to a given offset in + * the code. + * + * @author Eric Lafortune + */ +public class BranchInstruction extends Instruction +{ + public int branchOffset; + + + /** + * Creates an uninitialized BranchInstruction. + */ + public BranchInstruction() {} + + + public BranchInstruction(byte opcode, int branchOffset) + { + this.opcode = opcode; + this.branchOffset = branchOffset; + } + + + /** + * Copies the given instruction into this instruction. + * @param branchInstruction the instruction to be copied. + * @return this instruction. + */ + public BranchInstruction copy(BranchInstruction branchInstruction) + { + this.opcode = branchInstruction.opcode; + this.branchOffset = branchInstruction.branchOffset; + + return this; + } + + + // 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? + if (requiredBranchOffsetSize() == 2) + { + // Can we replace the wide branch by an ordinary branch? + if (opcode == InstructionConstants.OP_GOTO_W) + { + opcode = InstructionConstants.OP_GOTO; + } + else if (opcode == InstructionConstants.OP_JSR_W) + { + opcode = InstructionConstants.OP_JSR; + } + } + else + { + // Should we replace the ordinary branch by a wide branch? + if (opcode == InstructionConstants.OP_GOTO) + { + opcode = InstructionConstants.OP_GOTO_W; + } + else if (opcode == InstructionConstants.OP_JSR) + { + opcode = InstructionConstants.OP_JSR_W; + } + else + { + throw new IllegalArgumentException("Branch instruction can't be widened ("+this.toString()+")"); + } + } + + return this; + } + + protected void readInfo(byte[] code, int offset) + { + branchOffset = readSignedValue(code, offset, branchOffsetSize()); + } + + + protected void writeInfo(byte[] code, int offset) + { + if (requiredBranchOffsetSize() > branchOffsetSize()) + { + throw new IllegalArgumentException("Instruction has invalid branch offset size ("+this.toString(offset)+")"); + } + + writeSignedValue(code, offset, branchOffset, branchOffsetSize()); + } + + + public int length(int offset) + { + return 1 + branchOffsetSize(); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, this); + } + + + public String toString(int offset) + { + return "["+offset+"] "+toString()+" (target="+(offset+branchOffset)+")"; + } + + + // Implementations for Object. + + public String toString() + { + return getName()+" "+(branchOffset >= 0 ? "+" : "")+branchOffset; + } + + + // Small utility methods. + + /** + * Returns the branch offset size for this instruction. + */ + private int branchOffsetSize() + { + return opcode == InstructionConstants.OP_GOTO_W || + opcode == InstructionConstants.OP_JSR_W ? 4 : + 2; + } + + + /** + * Computes the required branch offset size for this instruction's branch + * offset. + */ + private int requiredBranchOffsetSize() + { + return (short)branchOffset == branchOffset ? 2 : 4; + } +} diff --git a/src/proguard/classfile/instruction/ConstantInstruction.java b/src/proguard/classfile/instruction/ConstantInstruction.java new file mode 100644 index 000000000..42d152315 --- /dev/null +++ b/src/proguard/classfile/instruction/ConstantInstruction.java @@ -0,0 +1,309 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.CodeAttribute; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.ClassUtil; + +/** + * This Instruction represents an instruction that refers to an entry in the + * constant pool. + * + * @author Eric Lafortune + */ +public class ConstantInstruction extends Instruction +implements ConstantVisitor +{ + public int constantIndex; + public int constant; + + + // Fields acting as return parameters for the ConstantVisitor methods. + private int parameterStackDelta; + private int typeStackDelta; + + + /** + * Creates an uninitialized ConstantInstruction. + */ + public ConstantInstruction() {} + + + /** + * Creates a new ConstantInstruction with the given opcode and constant pool + * index. + */ + public ConstantInstruction(byte opcode, int constantIndex) + { + this(opcode, constantIndex, 0); + } + + + /** + * Creates a new ConstantInstruction with the given opcode, constant pool + * index, and constant. + */ + public ConstantInstruction(byte opcode, int constantIndex, int constant) + { + this.opcode = opcode; + this.constantIndex = constantIndex; + this.constant = constant; + } + + + /** + * Copies the given instruction into this instruction. + * @param constantInstruction the instruction to be copied. + * @return this instruction. + */ + public ConstantInstruction copy(ConstantInstruction constantInstruction) + { + this.opcode = constantInstruction.opcode; + this.constantIndex = constantInstruction.constantIndex; + this.constant = constantInstruction.constant; + + return this; + } + + + // Implementations for Instruction. + + public byte canonicalOpcode() + { + // Remove the _w extension, if any. + return + opcode == InstructionConstants.OP_LDC_W ? InstructionConstants.OP_LDC : + opcode; + } + + public Instruction shrink() + { + // Do we need a short index or a long index? + if (requiredConstantIndexSize() == 1) + { + // Can we replace the long instruction by a short instruction? + if (opcode == InstructionConstants.OP_LDC_W) + { + opcode = InstructionConstants.OP_LDC; + } + } + else + { + // Should we replace the short instruction by a long instruction? + if (opcode == InstructionConstants.OP_LDC) + { + opcode = InstructionConstants.OP_LDC_W; + } + } + + return this; + } + + protected void readInfo(byte[] code, int offset) + { + int constantIndexSize = constantIndexSize(); + int constantSize = constantSize(); + + constantIndex = readValue(code, offset, constantIndexSize); offset += constantIndexSize; + constant = readValue(code, offset, constantSize); + } + + + protected void writeInfo(byte[] code, int offset) + { + int constantIndexSize = constantIndexSize(); + int constantSize = constantSize(); + + if (requiredConstantIndexSize() > constantIndexSize) + { + throw new IllegalArgumentException("Instruction has invalid constant index size ("+this.toString(offset)+")"); + } + + writeValue(code, offset, constantIndex, constantIndexSize); offset += constantIndexSize; + writeValue(code, offset, constant, constantSize); + } + + + public int length(int offset) + { + return 1 + constantIndexSize() + constantSize(); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, this); + } + + + public int stackPopCount(Clazz clazz) + { + int stackPopCount = super.stackPopCount(clazz); + + // Some special cases. + switch (opcode) + { + case InstructionConstants.OP_MULTIANEWARRAY: + // For each dimension, an integer size is popped from the stack. + stackPopCount += constant; + break; + + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_PUTFIELD: + // The field value is be popped from the stack. + clazz.constantPoolEntryAccept(constantIndex, this); + stackPopCount += typeStackDelta; + break; + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + // Some parameters may be popped from the stack. + clazz.constantPoolEntryAccept(constantIndex, this); + stackPopCount += parameterStackDelta; + break; + } + + return stackPopCount; + } + + + public int stackPushCount(Clazz clazz) + { + int stackPushCount = super.stackPushCount(clazz); + + // Some special cases. + switch (opcode) + { + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + // The field value or a return value may be pushed onto the stack. + clazz.constantPoolEntryAccept(constantIndex, this); + stackPushCount += typeStackDelta; + break; + } + + return stackPushCount; + } + + + // Implementations for 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 visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) {} + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) {} + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) {} + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + String type = fieldrefConstant.getType(clazz); + + typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type)); + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + clazz.constantPoolEntryAccept(invokeDynamicConstant.u2nameAndTypeIndex, this); + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2nameAndTypeIndex, this); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + clazz.constantPoolEntryAccept(methodrefConstant.u2nameAndTypeIndex, this); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + String type = nameAndTypeConstant.getType(clazz); + + parameterStackDelta = ClassUtil.internalMethodParameterSize(type); + typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type)); + } + + + // Implementations for Object. + + public String toString() + { + return getName()+" #"+constantIndex+(constantSize() == 0 ? "" : ", "+constant); + } + + + // Small utility methods. + + /** + * Returns the constant pool index size for this instruction. + */ + private int constantIndexSize() + { + return opcode == InstructionConstants.OP_LDC ? 1 : + 2; + } + + + /** + * Returns the constant size for this instruction. + */ + private int constantSize() + { + return opcode == InstructionConstants.OP_MULTIANEWARRAY ? 1 : + opcode == InstructionConstants.OP_INVOKEDYNAMIC || + opcode == InstructionConstants.OP_INVOKEINTERFACE ? 2 : + 0; + } + + + /** + * Computes the required constant pool index size for this instruction's + * constant pool index. + */ + private int requiredConstantIndexSize() + { + 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 new file mode 100644 index 000000000..33e705daf --- /dev/null +++ b/src/proguard/classfile/instruction/Instruction.java @@ -0,0 +1,920 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * Base class for representing instructions. + * + * @author Eric Lafortune + */ +public abstract class Instruction +{ + // An array for marking Category 2 instructions. + private static final boolean[] IS_CATEGORY2 = new boolean[] + { + false, // nop + false, // aconst_null + false, // iconst_m1 + false, // iconst_0 + false, // iconst_1 + false, // iconst_2 + false, // iconst_3 + false, // iconst_4 + false, // iconst_5 + true, // lconst_0 + true, // lconst_1 + false, // fconst_0 + false, // fconst_1 + false, // fconst_2 + true, // dconst_0 + true, // dconst_1 + false, // bipush + false, // sipush + false, // ldc + false, // ldc_w + true, // ldc2_w + false, // iload + true, // lload + false, // fload + true, // dload + false, // aload + false, // iload_0 + false, // iload_1 + false, // iload_2 + false, // iload_3 + true, // lload_0 + true, // lload_1 + true, // lload_2 + true, // lload_3 + false, // fload_0 + false, // fload_1 + false, // fload_2 + false, // fload_3 + true, // dload_0 + true, // dload_1 + true, // dload_2 + true, // dload_3 + false, // aload_0 + false, // aload_1 + false, // aload_2 + false, // aload_3 + false, // iaload + true, // laload + false, // faload + true, // daload + false, // aaload + false, // baload + false, // caload + false, // saload + false, // istore + true, // lstore + false, // fstore + true, // dstore + false, // astore + false, // istore_0 + false, // istore_1 + false, // istore_2 + false, // istore_3 + true, // lstore_0 + true, // lstore_1 + true, // lstore_2 + true, // lstore_3 + false, // fstore_0 + false, // fstore_1 + false, // fstore_2 + false, // fstore_3 + true, // dstore_0 + true, // dstore_1 + true, // dstore_2 + true, // dstore_3 + false, // astore_0 + false, // astore_1 + false, // astore_2 + false, // astore_3 + false, // iastore + true, // lastore + false, // fastore + true, // dastore + false, // aastore + false, // bastore + false, // castore + false, // sastore + false, // pop + true, // pop2 + false, // dup + false, // dup_x1 + false, // dup_x2 + true, // dup2 + true, // dup2_x1 + true, // dup2_x2 + false, // swap + false, // iadd + true, // ladd + false, // fadd + true, // dadd + false, // isub + true, // lsub + false, // fsub + true, // dsub + false, // imul + true, // lmul + false, // fmul + true, // dmul + false, // idiv + true, // ldiv + false, // fdiv + true, // ddiv + false, // irem + true, // lrem + false, // frem + true, // drem + false, // ineg + true, // lneg + false, // fneg + true, // dneg + false, // ishl + true, // lshl + false, // ishr + true, // lshr + false, // iushr + true, // lushr + false, // iand + true, // land + false, // ior + true, // lor + false, // ixor + true, // lxor + false, // iinc + false, // i2l + false, // i2f + false, // i2d + true, // l2i + true, // l2f + true, // l2d + false, // f2i + false, // f2l + false, // f2d + true, // d2i + true, // d2l + true, // d2f + false, // i2b + false, // i2c + false, // i2s + true, // lcmp + false, // fcmpl + false, // fcmpg + true, // dcmpl + true, // dcmpg + false, // ifeq + false, // ifne + false, // iflt + false, // ifge + false, // ifgt + false, // ifle + false, // ificmpeq + false, // ificmpne + false, // ificmplt + false, // ificmpge + false, // ificmpgt + false, // ificmple + false, // ifacmpeq + false, // ifacmpne + false, // goto + false, // jsr + false, // ret + false, // tableswitch + false, // lookupswitch + false, // ireturn + true, // lreturn + false, // freturn + true, // dreturn + false, // areturn + false, // return + false, // getstatic + false, // putstatic + false, // getfield + false, // putfield + false, // invokevirtual + false, // invokespecial + false, // invokestatic + false, // invokeinterface + false, // invokedynamic + false, // new + false, // newarray + false, // anewarray + false, // arraylength + false, // athrow + false, // checkcast + false, // instanceof + false, // monitorenter + false, // monitorexit + false, // wide + false, // multianewarray + false, // ifnull + false, // ifnonnull + false, // goto_w + false, // jsr_w + }; + + + // An array containing the fixed number of entries popped from the stack, + // for all instructions. + private static final int[] STACK_POP_COUNTS = new int[] + { + 0, // nop + 0, // aconst_null + 0, // iconst_m1 + 0, // iconst_0 + 0, // iconst_1 + 0, // iconst_2 + 0, // iconst_3 + 0, // iconst_4 + 0, // iconst_5 + 0, // lconst_0 + 0, // lconst_1 + 0, // fconst_0 + 0, // fconst_1 + 0, // fconst_2 + 0, // dconst_0 + 0, // dconst_1 + 0, // bipush + 0, // sipush + 0, // ldc + 0, // ldc_w + 0, // ldc2_w + 0, // iload + 0, // lload + 0, // fload + 0, // dload + 0, // aload + 0, // iload_0 + 0, // iload_1 + 0, // iload_2 + 0, // iload_3 + 0, // lload_0 + 0, // lload_1 + 0, // lload_2 + 0, // lload_3 + 0, // fload_0 + 0, // fload_1 + 0, // fload_2 + 0, // fload_3 + 0, // dload_0 + 0, // dload_1 + 0, // dload_2 + 0, // dload_3 + 0, // aload_0 + 0, // aload_1 + 0, // aload_2 + 0, // aload_3 + 2, // iaload + 2, // laload + 2, // faload + 2, // daload + 2, // aaload + 2, // baload + 2, // caload + 2, // saload + 1, // istore + 2, // lstore + 1, // fstore + 2, // dstore + 1, // astore + 1, // istore_0 + 1, // istore_1 + 1, // istore_2 + 1, // istore_3 + 2, // lstore_0 + 2, // lstore_1 + 2, // lstore_2 + 2, // lstore_3 + 1, // fstore_0 + 1, // fstore_1 + 1, // fstore_2 + 1, // fstore_3 + 2, // dstore_0 + 2, // dstore_1 + 2, // dstore_2 + 2, // dstore_3 + 1, // astore_0 + 1, // astore_1 + 1, // astore_2 + 1, // astore_3 + 3, // iastore + 4, // lastore + 3, // fastore + 4, // dastore + 3, // aastore + 3, // bastore + 3, // castore + 3, // sastore + 1, // pop + 2, // pop2 + 1, // dup + 2, // dup_x1 + 3, // dup_x2 + 2, // dup2 + 3, // dup2_x1 + 4, // dup2_x2 + 2, // swap + 2, // iadd + 4, // ladd + 2, // fadd + 4, // dadd + 2, // isub + 4, // lsub + 2, // fsub + 4, // dsub + 2, // imul + 4, // lmul + 2, // fmul + 4, // dmul + 2, // idiv + 4, // ldiv + 2, // fdiv + 4, // ddiv + 2, // irem + 4, // lrem + 2, // frem + 4, // drem + 1, // ineg + 2, // lneg + 1, // fneg + 2, // dneg + 2, // ishl + 3, // lshl + 2, // ishr + 3, // lshr + 2, // iushr + 3, // lushr + 2, // iand + 4, // land + 2, // ior + 4, // lor + 2, // ixor + 4, // lxor + 0, // iinc + 1, // i2l + 1, // i2f + 1, // i2d + 2, // l2i + 2, // l2f + 2, // l2d + 1, // f2i + 1, // f2l + 1, // f2d + 2, // d2i + 2, // d2l + 2, // d2f + 1, // i2b + 1, // i2c + 1, // i2s + 4, // lcmp + 2, // fcmpl + 2, // fcmpg + 4, // dcmpl + 4, // dcmpg + 1, // ifeq + 1, // ifne + 1, // iflt + 1, // ifge + 1, // ifgt + 1, // ifle + 2, // ificmpeq + 2, // ificmpne + 2, // ificmplt + 2, // ificmpge + 2, // ificmpgt + 2, // ificmple + 2, // ifacmpeq + 2, // ifacmpne + 0, // goto + 0, // jsr + 0, // ret + 1, // tableswitch + 1, // lookupswitch + 1, // ireturn + 2, // lreturn + 1, // freturn + 2, // dreturn + 1, // areturn + 0, // return + 0, // getstatic + 0, // putstatic + 1, // getfield + 1, // putfield + 1, // invokevirtual + 1, // invokespecial + 0, // invokestatic + 1, // invokeinterface + 0, // invokedynamic + 0, // new + 1, // newarray + 1, // anewarray + 1, // arraylength + 1, // athrow + 1, // checkcast + 1, // instanceof + 1, // monitorenter + 1, // monitorexit + 0, // wide + 0, // multianewarray + 1, // ifnull + 1, // ifnonnull + 0, // goto_w + 0, // jsr_w + }; + + + // An array containing the fixed number of entries pushed onto the stack, + // for all instructions. + private static final int[] STACK_PUSH_COUNTS = new int[] + { + 0, // nop + 1, // aconst_null + 1, // iconst_m1 + 1, // iconst_0 + 1, // iconst_1 + 1, // iconst_2 + 1, // iconst_3 + 1, // iconst_4 + 1, // iconst_5 + 2, // lconst_0 + 2, // lconst_1 + 1, // fconst_0 + 1, // fconst_1 + 1, // fconst_2 + 2, // dconst_0 + 2, // dconst_1 + 1, // bipush + 1, // sipush + 1, // ldc + 1, // ldc_w + 2, // ldc2_w + 1, // iload + 2, // lload + 1, // fload + 2, // dload + 1, // aload + 1, // iload_0 + 1, // iload_1 + 1, // iload_2 + 1, // iload_3 + 2, // lload_0 + 2, // lload_1 + 2, // lload_2 + 2, // lload_3 + 1, // fload_0 + 1, // fload_1 + 1, // fload_2 + 1, // fload_3 + 2, // dload_0 + 2, // dload_1 + 2, // dload_2 + 2, // dload_3 + 1, // aload_0 + 1, // aload_1 + 1, // aload_2 + 1, // aload_3 + 1, // iaload + 2, // laload + 1, // faload + 2, // daload + 1, // aaload + 1, // baload + 1, // caload + 1, // saload + 0, // istore + 0, // lstore + 0, // fstore + 0, // dstore + 0, // astore + 0, // istore_0 + 0, // istore_1 + 0, // istore_2 + 0, // istore_3 + 0, // lstore_0 + 0, // lstore_1 + 0, // lstore_2 + 0, // lstore_3 + 0, // fstore_0 + 0, // fstore_1 + 0, // fstore_2 + 0, // fstore_3 + 0, // dstore_0 + 0, // dstore_1 + 0, // dstore_2 + 0, // dstore_3 + 0, // astore_0 + 0, // astore_1 + 0, // astore_2 + 0, // astore_3 + 0, // iastore + 0, // lastore + 0, // fastore + 0, // dastore + 0, // aastore + 0, // bastore + 0, // castore + 0, // sastore + 0, // pop + 0, // pop2 + 2, // dup + 3, // dup_x1 + 4, // dup_x2 + 4, // dup2 + 5, // dup2_x1 + 6, // dup2_x2 + 2, // swap + 1, // iadd + 2, // ladd + 1, // fadd + 2, // dadd + 1, // isub + 2, // lsub + 1, // fsub + 2, // dsub + 1, // imul + 2, // lmul + 1, // fmul + 2, // dmul + 1, // idiv + 2, // ldiv + 1, // fdiv + 2, // ddiv + 1, // irem + 2, // lrem + 1, // frem + 2, // drem + 1, // ineg + 2, // lneg + 1, // fneg + 2, // dneg + 1, // ishl + 2, // lshl + 1, // ishr + 2, // lshr + 1, // iushr + 2, // lushr + 1, // iand + 2, // land + 1, // ior + 2, // lor + 1, // ixor + 2, // lxor + 0, // iinc + 2, // i2l + 1, // i2f + 2, // i2d + 1, // l2i + 1, // l2f + 2, // l2d + 1, // f2i + 2, // f2l + 2, // f2d + 1, // d2i + 2, // d2l + 1, // d2f + 1, // i2b + 1, // i2c + 1, // i2s + 1, // lcmp + 1, // fcmpl + 1, // fcmpg + 1, // dcmpl + 1, // dcmpg + 0, // ifeq + 0, // ifne + 0, // iflt + 0, // ifge + 0, // ifgt + 0, // ifle + 0, // ificmpeq + 0, // ificmpne + 0, // ificmplt + 0, // ificmpge + 0, // ificmpgt + 0, // ificmple + 0, // ifacmpeq + 0, // ifacmpne + 0, // goto + 1, // jsr + 0, // ret + 0, // tableswitch + 0, // lookupswitch + 0, // ireturn + 0, // lreturn + 0, // freturn + 0, // dreturn + 0, // areturn + 0, // return + 0, // getstatic + 0, // putstatic + 0, // getfield + 0, // putfield + 0, // invokevirtual + 0, // invokespecial + 0, // invokestatic + 0, // invokeinterface + 0, // invokedynamic + 1, // new + 1, // newarray + 1, // anewarray + 1, // arraylength + 0, // athrow + 1, // checkcast + 1, // instanceof + 0, // monitorenter + 0, // monitorexit + 0, // wide + 1, // multianewarray + 0, // ifnull + 0, // ifnonnull + 0, // goto_w + 1, // jsr_w + }; + + + public byte opcode; + + + /** + * 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. + */ + public abstract Instruction shrink(); + + + + /** + * Writes the Instruction at the given offset in the given code attribute. + */ + public final void write(CodeAttribute codeAttribute, int offset) + { + write(codeAttribute.code, offset); + } + + + /** + * Writes the Instruction at the given offset in the given code array. + */ + public void write(byte[] code, int offset) + { + // Write the wide opcode, if necessary. + if (isWide()) + { + code[offset++] = InstructionConstants.OP_WIDE; + } + + // Write the opcode. + code[offset++] = opcode; + + // Write any additional arguments. + writeInfo(code, offset); + } + + + /** + * Returns whether the instruction is wide, i.e. preceded by a wide opcode. + * With the current specifications, only variable instructions can be wide. + */ + protected boolean isWide() + { + return false; + } + + + /** + * Reads the data following the instruction opcode. + */ + protected abstract void readInfo(byte[] code, int offset); + + + /** + * Writes data following the instruction opcode. + */ + protected abstract void writeInfo(byte[] code, int offset); + + + /** + * Returns the length in bytes of the instruction. + */ + public abstract int length(int offset); + + + /** + * Accepts the given visitor. + */ + 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 String toString(int offset) + { + return "["+offset+"] "+ this.toString(); + } + + + /** + * Returns the name of the instruction. + */ + public String getName() + { + return InstructionConstants.NAMES[opcode & 0xff]; + } + + + /** + * Returns whether the instruction is a Category 2 instruction. This means + * that it operates on long or double arguments. + */ + public boolean isCategory2() + { + return IS_CATEGORY2[opcode & 0xff]; + } + + + /** + * Returns the number of entries popped from the stack during the execution + * of the instruction. + */ + public int stackPopCount(Clazz clazz) + { + return STACK_POP_COUNTS[opcode & 0xff]; + } + + + /** + * Returns the number of entries pushed onto the stack during the execution + * of the instruction. + */ + public int stackPushCount(Clazz clazz) + { + return STACK_PUSH_COUNTS[opcode & 0xff]; + } + + + // Small utility methods. + + protected static int readByte(byte[] code, int offset) + { + return code[offset] & 0xff; + } + + protected static int readShort(byte[] code, int offset) + { + return ((code[offset++] & 0xff) << 8) | + ( code[offset ] & 0xff ); + } + + protected static int readInt(byte[] code, int offset) + { + return ( code[offset++] << 24) | + ((code[offset++] & 0xff) << 16) | + ((code[offset++] & 0xff) << 8) | + ( code[offset ] & 0xff ); + } + + protected static int readValue(byte[] code, int offset, int valueSize) + { + switch (valueSize) + { + case 0: return 0; + case 1: return readByte( code, offset); + case 2: return readShort(code, offset); + case 4: return readInt( code, offset); + default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]"); + } + } + + protected static int readSignedByte(byte[] code, int offset) + { + return code[offset]; + } + + protected static int readSignedShort(byte[] code, int offset) + { + return (code[offset++] << 8) | + (code[offset ] & 0xff); + } + + protected static int readSignedValue(byte[] code, int offset, int valueSize) + { + switch (valueSize) + { + case 0: return 0; + case 1: return readSignedByte( code, offset); + case 2: return readSignedShort(code, offset); + case 4: return readInt( code, offset); + default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]"); + } + } + + protected static void writeByte(byte[] code, int offset, int value) + { + if (value > 0xff) + { + throw new IllegalArgumentException("Unsigned byte value larger than 0xff ["+value+"]"); + } + + code[offset] = (byte)value; + } + + protected static void writeShort(byte[] code, int offset, int value) + { + if (value > 0xffff) + { + throw new IllegalArgumentException("Unsigned short value larger than 0xffff ["+value+"]"); + } + + code[offset++] = (byte)(value >> 8); + code[offset ] = (byte)(value ); + } + + protected static void writeInt(byte[] code, int offset, int value) + { + code[offset++] = (byte)(value >> 24); + code[offset++] = (byte)(value >> 16); + code[offset++] = (byte)(value >> 8); + code[offset ] = (byte)(value ); + } + + protected static void writeValue(byte[] code, int offset, int value, int valueSize) + { + switch (valueSize) + { + case 0: break; + case 1: writeByte( code, offset, value); break; + case 2: writeShort(code, offset, value); break; + case 4: writeInt( code, offset, value); break; + default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]"); + } + } + + protected static void writeSignedByte(byte[] code, int offset, int value) + { + if ((byte)value != 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 ((short)value != 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 new file mode 100644 index 000000000..d8cbd9841 --- /dev/null +++ b/src/proguard/classfile/instruction/InstructionConstants.java @@ -0,0 +1,449 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * Representation of an instruction. + * + * @author Eric Lafortune + */ +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_INVOKEDYNAMIC = -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 String[] NAMES = + { + "nop", + "aconst_null", + "iconst_m1", + "iconst_0", + "iconst_1", + "iconst_2", + "iconst_3", + "iconst_4", + "iconst_5", + "lconst_0", + "lconst_1", + "fconst_0", + "fconst_1", + "fconst_2", + "dconst_0", + "dconst_1", + "bipush", + "sipush", + "ldc", + "ldc_w", + "ldc2_w", + "iload", + "lload", + "fload", + "dload", + "aload", + "iload_0", + "iload_1", + "iload_2", + "iload_3", + "lload_0", + "lload_1", + "lload_2", + "lload_3", + "fload_0", + "fload_1", + "fload_2", + "fload_3", + "dload_0", + "dload_1", + "dload_2", + "dload_3", + "aload_0", + "aload_1", + "aload_2", + "aload_3", + "iaload", + "laload", + "faload", + "daload", + "aaload", + "baload", + "caload", + "saload", + "istore", + "lstore", + "fstore", + "dstore", + "astore", + "istore_0", + "istore_1", + "istore_2", + "istore_3", + "lstore_0", + "lstore_1", + "lstore_2", + "lstore_3", + "fstore_0", + "fstore_1", + "fstore_2", + "fstore_3", + "dstore_0", + "dstore_1", + "dstore_2", + "dstore_3", + "astore_0", + "astore_1", + "astore_2", + "astore_3", + "iastore", + "lastore", + "fastore", + "dastore", + "aastore", + "bastore", + "castore", + "sastore", + "pop", + "pop2", + "dup", + "dup_x1", + "dup_x2", + "dup2", + "dup2_x1", + "dup2_x2", + "swap", + "iadd", + "ladd", + "fadd", + "dadd", + "isub", + "lsub", + "fsub", + "dsub", + "imul", + "lmul", + "fmul", + "dmul", + "idiv", + "ldiv", + "fdiv", + "ddiv", + "irem", + "lrem", + "frem", + "drem", + "ineg", + "lneg", + "fneg", + "dneg", + "ishl", + "lshl", + "ishr", + "lshr", + "iushr", + "lushr", + "iand", + "land", + "ior", + "lor", + "ixor", + "lxor", + "iinc", + "i2l", + "i2f", + "i2d", + "l2i", + "l2f", + "l2d", + "f2i", + "f2l", + "f2d", + "d2i", + "d2l", + "d2f", + "i2b", + "i2c", + "i2s", + "lcmp", + "fcmpl", + "fcmpg", + "dcmpl", + "dcmpg", + "ifeq", + "ifne", + "iflt", + "ifge", + "ifgt", + "ifle", + "ificmpeq", + "ificmpne", + "ificmplt", + "ificmpge", + "ificmpgt", + "ificmple", + "ifacmpeq", + "ifacmpne", + "goto", + "jsr", + "ret", + "tableswitch", + "lookupswitch", + "ireturn", + "lreturn", + "freturn", + "dreturn", + "areturn", + "return", + "getstatic", + "putstatic", + "getfield", + "putfield", + "invokevirtual", + "invokespecial", + "invokestatic", + "invokeinterface", + "invokedynamic", + "new", + "newarray", + "anewarray", + "arraylength", + "athrow", + "checkcast", + "instanceof", + "monitorenter", + "monitorexit", + "wide", + "multianewarray", + "ifnull", + "ifnonnull", + "goto_w", + "jsr_w", + }; + + + 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/InstructionFactory.java b/src/proguard/classfile/instruction/InstructionFactory.java new file mode 100644 index 000000000..89d498c60 --- /dev/null +++ b/src/proguard/classfile/instruction/InstructionFactory.java @@ -0,0 +1,300 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 class provides methods to create and reuse Instruction objects. + * + * @author Eric Lafortune + */ +public class InstructionFactory +{ + /** + * Creates a new Instruction from the data in the byte array, starting + * at the given index. + */ + public static Instruction create(byte[] code, int offset) + { + Instruction instruction; + + int index = offset; + byte opcode = code[index++]; + + boolean wide = false; + if (opcode == InstructionConstants.OP_WIDE) + { + opcode = code[index++]; + wide = true; + } + + switch (opcode) + { + // Simple instructions. + case InstructionConstants.OP_NOP: + case InstructionConstants.OP_ACONST_NULL: + case InstructionConstants.OP_ICONST_M1: + case InstructionConstants.OP_ICONST_0: + case InstructionConstants.OP_ICONST_1: + case InstructionConstants.OP_ICONST_2: + case InstructionConstants.OP_ICONST_3: + case InstructionConstants.OP_ICONST_4: + case InstructionConstants.OP_ICONST_5: + case InstructionConstants.OP_LCONST_0: + case InstructionConstants.OP_LCONST_1: + case InstructionConstants.OP_FCONST_0: + case InstructionConstants.OP_FCONST_1: + case InstructionConstants.OP_FCONST_2: + case InstructionConstants.OP_DCONST_0: + case InstructionConstants.OP_DCONST_1: + + case InstructionConstants.OP_BIPUSH: + case InstructionConstants.OP_SIPUSH: + + case InstructionConstants.OP_IALOAD: + case InstructionConstants.OP_LALOAD: + case InstructionConstants.OP_FALOAD: + case InstructionConstants.OP_DALOAD: + case InstructionConstants.OP_AALOAD: + case InstructionConstants.OP_BALOAD: + case InstructionConstants.OP_CALOAD: + case InstructionConstants.OP_SALOAD: + + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_AASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + case InstructionConstants.OP_POP: + case InstructionConstants.OP_POP2: + case InstructionConstants.OP_DUP: + case InstructionConstants.OP_DUP_X1: + case InstructionConstants.OP_DUP_X2: + case InstructionConstants.OP_DUP2: + case InstructionConstants.OP_DUP2_X1: + case InstructionConstants.OP_DUP2_X2: + case InstructionConstants.OP_SWAP: + case InstructionConstants.OP_IADD: + case InstructionConstants.OP_LADD: + case InstructionConstants.OP_FADD: + case InstructionConstants.OP_DADD: + case InstructionConstants.OP_ISUB: + case InstructionConstants.OP_LSUB: + case InstructionConstants.OP_FSUB: + case InstructionConstants.OP_DSUB: + case InstructionConstants.OP_IMUL: + case InstructionConstants.OP_LMUL: + case InstructionConstants.OP_FMUL: + case InstructionConstants.OP_DMUL: + case InstructionConstants.OP_IDIV: + case InstructionConstants.OP_LDIV: + case InstructionConstants.OP_FDIV: + case InstructionConstants.OP_DDIV: + case InstructionConstants.OP_IREM: + case InstructionConstants.OP_LREM: + case InstructionConstants.OP_FREM: + case InstructionConstants.OP_DREM: + case InstructionConstants.OP_INEG: + case InstructionConstants.OP_LNEG: + case InstructionConstants.OP_FNEG: + case InstructionConstants.OP_DNEG: + case InstructionConstants.OP_ISHL: + case InstructionConstants.OP_LSHL: + case InstructionConstants.OP_ISHR: + case InstructionConstants.OP_LSHR: + case InstructionConstants.OP_IUSHR: + case InstructionConstants.OP_LUSHR: + case InstructionConstants.OP_IAND: + case InstructionConstants.OP_LAND: + case InstructionConstants.OP_IOR: + case InstructionConstants.OP_LOR: + case InstructionConstants.OP_IXOR: + case InstructionConstants.OP_LXOR: + + case InstructionConstants.OP_I2L: + case InstructionConstants.OP_I2F: + case InstructionConstants.OP_I2D: + case InstructionConstants.OP_L2I: + case InstructionConstants.OP_L2F: + case InstructionConstants.OP_L2D: + case InstructionConstants.OP_F2I: + case InstructionConstants.OP_F2L: + case InstructionConstants.OP_F2D: + case InstructionConstants.OP_D2I: + case InstructionConstants.OP_D2L: + case InstructionConstants.OP_D2F: + case InstructionConstants.OP_I2B: + case InstructionConstants.OP_I2C: + case InstructionConstants.OP_I2S: + case InstructionConstants.OP_LCMP: + case InstructionConstants.OP_FCMPL: + case InstructionConstants.OP_FCMPG: + case InstructionConstants.OP_DCMPL: + case InstructionConstants.OP_DCMPG: + + case InstructionConstants.OP_IRETURN: + case InstructionConstants.OP_LRETURN: + case InstructionConstants.OP_FRETURN: + case InstructionConstants.OP_DRETURN: + case InstructionConstants.OP_ARETURN: + case InstructionConstants.OP_RETURN: + + case InstructionConstants.OP_NEWARRAY: + case InstructionConstants.OP_ARRAYLENGTH: + case InstructionConstants.OP_ATHROW: + + case InstructionConstants.OP_MONITORENTER: + case InstructionConstants.OP_MONITOREXIT: + instruction = new SimpleInstruction(); + break; + + // Instructions with a contant pool index. + case InstructionConstants.OP_LDC: + case InstructionConstants.OP_LDC_W: + case InstructionConstants.OP_LDC2_W: + + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_PUTFIELD: + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + + case InstructionConstants.OP_NEW: + case InstructionConstants.OP_ANEWARRAY: + case InstructionConstants.OP_CHECKCAST: + case InstructionConstants.OP_INSTANCEOF: + case InstructionConstants.OP_MULTIANEWARRAY: + instruction = new ConstantInstruction(); + break; + + // Instructions with a local variable index. + case InstructionConstants.OP_ILOAD: + case InstructionConstants.OP_LLOAD: + case InstructionConstants.OP_FLOAD: + case InstructionConstants.OP_DLOAD: + case InstructionConstants.OP_ALOAD: + case InstructionConstants.OP_ILOAD_0: + case InstructionConstants.OP_ILOAD_1: + case InstructionConstants.OP_ILOAD_2: + case InstructionConstants.OP_ILOAD_3: + case InstructionConstants.OP_LLOAD_0: + case InstructionConstants.OP_LLOAD_1: + case InstructionConstants.OP_LLOAD_2: + case InstructionConstants.OP_LLOAD_3: + case InstructionConstants.OP_FLOAD_0: + case InstructionConstants.OP_FLOAD_1: + case InstructionConstants.OP_FLOAD_2: + case InstructionConstants.OP_FLOAD_3: + case InstructionConstants.OP_DLOAD_0: + case InstructionConstants.OP_DLOAD_1: + case InstructionConstants.OP_DLOAD_2: + case InstructionConstants.OP_DLOAD_3: + case InstructionConstants.OP_ALOAD_0: + case InstructionConstants.OP_ALOAD_1: + case InstructionConstants.OP_ALOAD_2: + case InstructionConstants.OP_ALOAD_3: + + case InstructionConstants.OP_ISTORE: + case InstructionConstants.OP_LSTORE: + case InstructionConstants.OP_FSTORE: + case InstructionConstants.OP_DSTORE: + case InstructionConstants.OP_ASTORE: + case InstructionConstants.OP_ISTORE_0: + case InstructionConstants.OP_ISTORE_1: + case InstructionConstants.OP_ISTORE_2: + case InstructionConstants.OP_ISTORE_3: + case InstructionConstants.OP_LSTORE_0: + case InstructionConstants.OP_LSTORE_1: + case InstructionConstants.OP_LSTORE_2: + case InstructionConstants.OP_LSTORE_3: + case InstructionConstants.OP_FSTORE_0: + case InstructionConstants.OP_FSTORE_1: + case InstructionConstants.OP_FSTORE_2: + case InstructionConstants.OP_FSTORE_3: + case InstructionConstants.OP_DSTORE_0: + case InstructionConstants.OP_DSTORE_1: + case InstructionConstants.OP_DSTORE_2: + case InstructionConstants.OP_DSTORE_3: + case InstructionConstants.OP_ASTORE_0: + case InstructionConstants.OP_ASTORE_1: + case InstructionConstants.OP_ASTORE_2: + case InstructionConstants.OP_ASTORE_3: + + case InstructionConstants.OP_IINC: + + case InstructionConstants.OP_RET: + instruction = new VariableInstruction(wide); + break; + + // Instructions with a branch offset operand. + case InstructionConstants.OP_IFEQ: + case InstructionConstants.OP_IFNE: + case InstructionConstants.OP_IFLT: + case InstructionConstants.OP_IFGE: + case InstructionConstants.OP_IFGT: + case InstructionConstants.OP_IFLE: + case InstructionConstants.OP_IFICMPEQ: + case InstructionConstants.OP_IFICMPNE: + case InstructionConstants.OP_IFICMPLT: + case InstructionConstants.OP_IFICMPGE: + case InstructionConstants.OP_IFICMPGT: + case InstructionConstants.OP_IFICMPLE: + case InstructionConstants.OP_IFACMPEQ: + case InstructionConstants.OP_IFACMPNE: + case InstructionConstants.OP_GOTO: + case InstructionConstants.OP_JSR: + + case InstructionConstants.OP_IFNULL: + case InstructionConstants.OP_IFNONNULL: + + case InstructionConstants.OP_GOTO_W: + case InstructionConstants.OP_JSR_W: + instruction = new BranchInstruction(); + break; + + // The tableswitch instruction. + case InstructionConstants.OP_TABLESWITCH: + instruction = new TableSwitchInstruction(); + break; + + // The lookupswitch instruction. + case InstructionConstants.OP_LOOKUPSWITCH: + instruction = new LookUpSwitchInstruction(); + break; + + default: + throw new IllegalArgumentException("Unknown instruction opcode ["+opcode+"] at offset "+offset); + } + + instruction.opcode = opcode; + + instruction.readInfo(code, index); + + return instruction; + } +} diff --git a/src/proguard/classfile/instruction/InstructionUtil.java b/src/proguard/classfile/instruction/InstructionUtil.java new file mode 100644 index 000000000..c6ae99bfa --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 InstructionConstants.ARRAY_T_BOOLEAN, + * InstructionConstants.ARRAY_T_BYTE, + * InstructionConstants.ARRAY_T_CHAR, + * InstructionConstants.ARRAY_T_SHORT, + * InstructionConstants.ARRAY_T_INT, + * InstructionConstants.ARRAY_T_LONG, + * InstructionConstants.ARRAY_T_FLOAT, or + * InstructionConstants.ARRAY_T_DOUBLE. + * @return 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, or + * ClassConstants.INTERNAL_TYPE_DOUBLE. + */ + 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/LookUpSwitchInstruction.java b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java new file mode 100644 index 000000000..807c0cceb --- /dev/null +++ b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java @@ -0,0 +1,135 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Instruction represents a simple instruction without variable arguments + * or constant pool references. + * + * @author Eric Lafortune + */ +public class LookUpSwitchInstruction extends SwitchInstruction +{ + public int[] cases; + + + /** + * Creates an uninitialized LookUpSwitchInstruction. + */ + public LookUpSwitchInstruction() {} + + + /** + * 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.cases = lookUpSwitchInstruction.cases; + this.jumpOffsets = lookUpSwitchInstruction.jumpOffsets; + + return this; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + // There aren't any ways to shrink this instruction. + return this; + } + + protected void readInfo(byte[] code, int offset) + { + // Skip up to three padding bytes. + offset += -offset & 3; + + // Read the two 32-bit arguments. + 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; + jumpOffsets[index] = readInt(code, offset); offset += 4; + } + } + + + protected void writeInfo(byte[] code, int offset) + { + // Write up to three padding bytes. + while ((offset & 3) != 0) + { + writeByte(code, offset++, 0); + } + + // Write the two 32-bit arguments. + writeInt(code, offset, defaultOffset); offset += 4; + writeInt(code, offset, cases.length); offset += 4; + + // Write the matches-offset pairs. + for (int index = 0; index < cases.length; index++) + { + writeInt(code, offset, cases[index]); offset += 4; + writeInt(code, offset, jumpOffsets[index]); offset += 4; + } + } + + + public int length(int offset) + { + return 1 + (-(offset+1) & 3) + 8 + cases.length * 8; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitLookUpSwitchInstruction(clazz, method, codeAttribute, offset, this); + } +} diff --git a/src/proguard/classfile/instruction/SimpleInstruction.java b/src/proguard/classfile/instruction/SimpleInstruction.java new file mode 100644 index 000000000..c9a29577b --- /dev/null +++ b/src/proguard/classfile/instruction/SimpleInstruction.java @@ -0,0 +1,255 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Instruction represents a simple instruction without variable arguments + * or constant pool references. + * + * @author Eric Lafortune + */ +public class SimpleInstruction extends Instruction +{ + public int constant; + + + /** + * Creates an uninitialized SimpleInstruction. + */ + public SimpleInstruction() {} + + + /** + * Creates a new SimpleInstruction with the given opcode. + */ + public SimpleInstruction(byte opcode) + { + this(opcode, embeddedConstant(opcode)); + } + + + /** + * Creates a new SimpleInstruction with the given opcode and constant. + */ + public SimpleInstruction(byte opcode, int constant) + { + this.opcode = opcode; + this.constant = constant; + } + + + /** + * Copies the given instruction into this instruction. + * @param simpleInstruction the instruction to be copied. + * @return this instruction. + */ + public SimpleInstruction copy(SimpleInstruction simpleInstruction) + { + this.opcode = simpleInstruction.opcode; + this.constant = simpleInstruction.constant; + + return this; + } + + + /** + * 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 byte canonicalOpcode() + { + // Replace any _1, _2, _3,... extension by _0. + switch (opcode) + { + case InstructionConstants.OP_ICONST_M1: + case InstructionConstants.OP_ICONST_0: + case InstructionConstants.OP_ICONST_1: + case InstructionConstants.OP_ICONST_2: + case InstructionConstants.OP_ICONST_3: + case InstructionConstants.OP_ICONST_4: + case InstructionConstants.OP_ICONST_5: + case InstructionConstants.OP_BIPUSH: + case InstructionConstants.OP_SIPUSH: return InstructionConstants.OP_ICONST_0; + + 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; + } + } + + public Instruction shrink() + { + // Reconstruct the opcode of the shortest instruction, if there are + // any alternatives. + switch (opcode) + { + case InstructionConstants.OP_ICONST_M1: + case InstructionConstants.OP_ICONST_0: + case InstructionConstants.OP_ICONST_1: + case InstructionConstants.OP_ICONST_2: + case InstructionConstants.OP_ICONST_3: + case InstructionConstants.OP_ICONST_4: + case InstructionConstants.OP_ICONST_5: + case InstructionConstants.OP_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_LCONST_0: + case InstructionConstants.OP_LCONST_1: + opcode = (byte)(InstructionConstants.OP_LCONST_0 + constant); + break; + + case InstructionConstants.OP_FCONST_0: + case InstructionConstants.OP_FCONST_1: + case InstructionConstants.OP_FCONST_2: + opcode = (byte)(InstructionConstants.OP_FCONST_0 + constant); + break; + + case InstructionConstants.OP_DCONST_0: + case InstructionConstants.OP_DCONST_1: + opcode = (byte)(InstructionConstants.OP_DCONST_0 + constant); + break; + } + + return this; + } + + protected void readInfo(byte[] code, int offset) + { + int constantSize = constantSize(); + + // Also initialize embedded constants that are different from 0. + constant = constantSize == 0 ? + embeddedConstant(opcode) : + readSignedValue(code, offset, constantSize); + } + + + protected void writeInfo(byte[] code, int offset) + { + int constantSize = constantSize(); + + if (requiredConstantSize() > constantSize) + { + throw new IllegalArgumentException("Instruction has invalid constant size ("+this.toString(offset)+")"); + } + + writeSignedValue(code, offset, constant, constantSize); + } + + + public int length(int offset) + { + return 1 + constantSize(); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitSimpleInstruction(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public String toString() + { + return getName() + + (constantSize() > 0 ? " "+constant : ""); + } + + + // Small utility methods. + + /** + * Returns the constant size for this instruction. + */ + private int constantSize() + { + return opcode == InstructionConstants.OP_BIPUSH || + opcode == InstructionConstants.OP_NEWARRAY ? 1 : + opcode == InstructionConstants.OP_SIPUSH ? 2 : + 0; + } + + + /** + * Computes the required constant size for this instruction. + */ + private int requiredConstantSize() + { + return constant >= -1 && constant <= 5 ? 0 : + (byte)constant == constant ? 1 : + (short)constant == constant ? 2 : + 4; + } +} diff --git a/src/proguard/classfile/instruction/SwitchInstruction.java b/src/proguard/classfile/instruction/SwitchInstruction.java new file mode 100644 index 000000000..3706ba025 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 new file mode 100644 index 000000000..45490e520 --- /dev/null +++ b/src/proguard/classfile/instruction/TableSwitchInstruction.java @@ -0,0 +1,139 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Instruction represents a simple instruction without variable arguments + * or constant pool references. + * + * @author Eric Lafortune + */ +public class TableSwitchInstruction extends SwitchInstruction +{ + public int lowCase; + public int highCase; + + + /** + * Creates an uninitialized TableSwitchInstruction. + */ + public TableSwitchInstruction() {} + + + /** + * 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.jumpOffsets = tableSwitchInstruction.jumpOffsets; + + return this; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + // There aren't any ways to shrink this instruction. + return this; + } + + protected void readInfo(byte[] code, int offset) + { + // Skip up to three padding bytes. + offset += -offset & 3; + + // Read the three 32-bit arguments. + defaultOffset = readInt(code, offset); offset += 4; + lowCase = readInt(code, offset); offset += 4; + highCase = readInt(code, offset); offset += 4; + + // Read the jump offsets. + jumpOffsets = new int[highCase - lowCase + 1]; + + for (int index = 0; index < jumpOffsets.length; index++) + { + jumpOffsets[index] = readInt(code, offset); offset += 4; + } + } + + + protected void writeInfo(byte[] code, int offset) + { + // Write up to three padding bytes. + while ((offset & 3) != 0) + { + writeByte(code, offset++, 0); + } + + // Write the three 32-bit arguments. + writeInt(code, offset, defaultOffset); offset += 4; + writeInt(code, offset, lowCase); offset += 4; + writeInt(code, offset, highCase); offset += 4; + + // Write the jump offsets. + int length = highCase - lowCase + 1; + for (int index = 0; index < length; index++) + { + writeInt(code, offset, jumpOffsets[index]); offset += 4; + } + } + + + public int length(int offset) + { + return 1 + (-(offset+1) & 3) + 12 + (highCase - lowCase + 1) * 4; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitTableSwitchInstruction(clazz, method, codeAttribute, offset, this); + } +} diff --git a/src/proguard/classfile/instruction/VariableInstruction.java b/src/proguard/classfile/instruction/VariableInstruction.java new file mode 100644 index 000000000..6390e0ad3 --- /dev/null +++ b/src/proguard/classfile/instruction/VariableInstruction.java @@ -0,0 +1,372 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.CodeAttribute; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This Instruction represents an instruction that refers to a variable on the + * local variable stack. + * + * @author Eric Lafortune + */ +public class VariableInstruction extends Instruction +{ + public boolean wide; + public int variableIndex; + public int constant; + + + /** + * Creates an uninitialized VariableInstruction. + */ + public VariableInstruction() {} + + + public VariableInstruction(boolean wide) + { + this.wide = wide; + } + + + public VariableInstruction(byte opcode) + { + this(opcode, embeddedVariable(opcode), 0); + } + + + public VariableInstruction(byte opcode, + int variableIndex) + { + this(opcode, variableIndex, 0); + } + + + public VariableInstruction(byte opcode, + int variableIndex, + int constant) + { + this.opcode = opcode; + this.variableIndex = variableIndex; + this.constant = constant; + this.wide = requiredVariableIndexSize() > 1 || + requiredConstantSize() > 1; + } + + + /** + * Copies the given instruction into this instruction. + * @param variableInstruction the instruction to be copied. + * @return this instruction. + */ + public VariableInstruction copy(VariableInstruction variableInstruction) + { + this.opcode = variableInstruction.opcode; + this.variableIndex = variableInstruction.variableIndex; + this.constant = variableInstruction.constant; + this.wide = variableInstruction.wide; + + return this; + } + + + /** + * 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 stores the value of a variable. + * The value is false for the ret instruction, but true for the iinc + * instruction. + */ + public boolean isStore() + { + // A store instruction can be recognized as follows. Note that this + // excludes the ret instruction, which has a negative opcode. + return opcode >= InstructionConstants.OP_ISTORE || + opcode == InstructionConstants.OP_IINC; + } + + + /** + * Returns whether this instruction loads the value of a variable. + * The value is true for the ret instruction and for the iinc + * instruction. + */ + public boolean isLoad() + { + // A load instruction can be recognized as follows. Note that this + // includes the ret instruction, which has a negative opcode. + return opcode < InstructionConstants.OP_ISTORE; + } + + + // Implementations for Instruction. + + public byte canonicalOpcode() + { + // 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: return InstructionConstants.OP_ILOAD; + case InstructionConstants.OP_LLOAD_0: + case InstructionConstants.OP_LLOAD_1: + case InstructionConstants.OP_LLOAD_2: + 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: return InstructionConstants.OP_FLOAD; + case InstructionConstants.OP_DLOAD_0: + case InstructionConstants.OP_DLOAD_1: + case InstructionConstants.OP_DLOAD_2: + 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: return InstructionConstants.OP_ALOAD; + + case InstructionConstants.OP_ISTORE_0: + case InstructionConstants.OP_ISTORE_1: + case InstructionConstants.OP_ISTORE_2: + 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: return InstructionConstants.OP_LSTORE; + case InstructionConstants.OP_FSTORE_0: + case InstructionConstants.OP_FSTORE_1: + case InstructionConstants.OP_FSTORE_2: + 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: return InstructionConstants.OP_DSTORE; + case InstructionConstants.OP_ASTORE_0: + case InstructionConstants.OP_ASTORE_1: + case InstructionConstants.OP_ASTORE_2: + 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) + { + switch (opcode) + { + case InstructionConstants.OP_ILOAD: opcode = (byte)(InstructionConstants.OP_ILOAD_0 + variableIndex); break; + case InstructionConstants.OP_LLOAD: opcode = (byte)(InstructionConstants.OP_LLOAD_0 + variableIndex); break; + case InstructionConstants.OP_FLOAD: opcode = (byte)(InstructionConstants.OP_FLOAD_0 + variableIndex); break; + case InstructionConstants.OP_DLOAD: opcode = (byte)(InstructionConstants.OP_DLOAD_0 + variableIndex); break; + case InstructionConstants.OP_ALOAD: opcode = (byte)(InstructionConstants.OP_ALOAD_0 + variableIndex); break; + + case InstructionConstants.OP_ISTORE: opcode = (byte)(InstructionConstants.OP_ISTORE_0 + variableIndex); break; + case InstructionConstants.OP_LSTORE: opcode = (byte)(InstructionConstants.OP_LSTORE_0 + variableIndex); break; + case InstructionConstants.OP_FSTORE: opcode = (byte)(InstructionConstants.OP_FSTORE_0 + variableIndex); break; + case InstructionConstants.OP_DSTORE: opcode = (byte)(InstructionConstants.OP_DSTORE_0 + variableIndex); break; + case InstructionConstants.OP_ASTORE: opcode = (byte)(InstructionConstants.OP_ASTORE_0 + variableIndex); break; + } + } + + // Only make the instruction wide if necessary. + wide = requiredVariableIndexSize() > 1 || + requiredConstantSize() > 1; + + return this; + } + + + protected boolean isWide() + { + return wide; + } + + + protected void readInfo(byte[] code, int offset) + { + int variableIndexSize = variableIndexSize(); + int constantSize = constantSize(); + + // Also initialize embedded variable indexes. + if (variableIndexSize == 0) + { + // An embedded variable index can be decoded as follows. + variableIndex = opcode < InstructionConstants.OP_ISTORE_0 ? + (opcode - InstructionConstants.OP_ILOAD_0 ) & 3 : + (opcode - InstructionConstants.OP_ISTORE_0) & 3; + } + else + { + variableIndex = readValue(code, offset, variableIndexSize); offset += variableIndexSize; + } + + constant = readSignedValue(code, offset, constantSize); + } + + + protected void writeInfo(byte[] code, int offset) + { + int variableIndexSize = variableIndexSize(); + int constantSize = constantSize(); + + if (requiredVariableIndexSize() > variableIndexSize) + { + throw new IllegalArgumentException("Instruction has invalid variable index size ("+this.toString(offset)+")"); + } + + if (requiredConstantSize() > constantSize) + { + throw new IllegalArgumentException("Instruction has invalid constant size ("+this.toString(offset)+")"); + } + + writeValue(code, offset, variableIndex, variableIndexSize); offset += variableIndexSize; + writeSignedValue(code, offset, constant, constantSize); + } + + + public int length(int offset) + { + return (wide ? 2 : 1) + variableIndexSize() + constantSize(); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + instructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, this); + } + + + // Implementations for Object. + + public String toString() + { + return getName() + + (wide ? "_w" : "") + + " v"+variableIndex + + (constantSize() > 0 ? ", "+constant : ""); + } + + + // Small utility methods. + + /** + * Returns the variable index size for this instruction. + */ + private int variableIndexSize() + { + return (opcode >= InstructionConstants.OP_ILOAD_0 && + opcode <= InstructionConstants.OP_ALOAD_3) || + (opcode >= InstructionConstants.OP_ISTORE_0 && + opcode <= InstructionConstants.OP_ASTORE_3) ? 0 : + wide ? 2 : + 1; + } + + + /** + * Computes the required variable index size for this instruction's variable + * index. + */ + private int requiredVariableIndexSize() + { + return (variableIndex & 0x3) == variableIndex ? 0 : + (variableIndex & 0xff) == variableIndex ? 1 : + (variableIndex & 0xffff) == variableIndex ? 2 : + 4; + + } + + + /** + * Returns the constant size for this instruction. + */ + private int constantSize() + { + return opcode != InstructionConstants.OP_IINC ? 0 : + wide ? 2 : + 1; + } + + + /** + * Computes the required constant size for this instruction's constant. + */ + private int requiredConstantSize() + { + return opcode != InstructionConstants.OP_IINC ? 0 : + (byte)constant == constant ? 1 : + (short)constant == constant ? 2 : + 4; + } +} diff --git a/src/proguard/classfile/instruction/package.html b/src/proguard/classfile/instruction/package.html new file mode 100644 index 000000000..48c234e3c --- /dev/null +++ b/src/proguard/classfile/instruction/package.html @@ -0,0 +1,9 @@ + +This package contains classes to represent Java bytecode instructions. +

+Not every instruction currently has its own class. Only groups of instructions +that refer to the constant pool get their own representations. +

+While the package is sufficient for the current needs of the ProGuard +application, it may very well be reorganised and extended in the future. + diff --git a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java b/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java new file mode 100644 index 000000000..4d739c25e --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/instruction/visitor/InstructionConstantVisitor.java b/src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java new file mode 100644 index 000000000..6b24e98c7 --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor lets a given ConstantVisitor visit all constants + * of the instructions it visits. + * + * @author Eric Lafortune + */ +public class InstructionConstantVisitor +extends SimplifiedVisitor +implements InstructionVisitor +{ + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new InstructionConstantVisitor. + * @param constantVisitor the ConstantVisitor to which visits will be + * delegated. + */ + public InstructionConstantVisitor(ConstantVisitor constantVisitor) + { + this.constantVisitor = constantVisitor; + } + + + // 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, + constantVisitor); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/instruction/visitor/InstructionCounter.java b/src/proguard/classfile/instruction/visitor/InstructionCounter.java new file mode 100644 index 000000000..562349865 --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/InstructionCounter.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.Instruction; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor counts the number of instructions that has been visited. + * + * @author Eric Lafortune + */ +public class InstructionCounter +extends SimplifiedVisitor +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 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 000000000..cffb15e18 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 + * Instruction 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/visitor/MultiInstructionVisitor.java b/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java new file mode 100644 index 000000000..444fd57c4 --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java @@ -0,0 +1,131 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 InstructionVisitor delegates all visits to each InstructionVisitor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiInstructionVisitor implements InstructionVisitor +{ + private static final int ARRAY_SIZE_INCREMENT = 5; + + + private InstructionVisitor[] instructionVisitors; + private int instructionVisitorCount; + + + public MultiInstructionVisitor() + { + } + + + public MultiInstructionVisitor(InstructionVisitor[] instructionVisitors) + { + this.instructionVisitors = instructionVisitors; + this.instructionVisitorCount = instructionVisitors.length; + } + + + public void addInstructionVisitor(InstructionVisitor instructionVisitor) + { + ensureArraySize(); + + instructionVisitors[instructionVisitorCount++] = instructionVisitor; + } + + + private void ensureArraySize() + { + if (instructionVisitors == null) + { + instructionVisitors = new InstructionVisitor[ARRAY_SIZE_INCREMENT]; + } + else if (instructionVisitors.length == instructionVisitorCount) + { + InstructionVisitor[] newInstructionVisitors = + new InstructionVisitor[instructionVisitorCount + + ARRAY_SIZE_INCREMENT]; + System.arraycopy(instructionVisitors, 0, + newInstructionVisitors, 0, + instructionVisitorCount); + instructionVisitors = newInstructionVisitors; + } + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitSimpleInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + } + } + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction); + } + } + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } + } + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + } + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + instructionVisitors[index].visitTableSwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction); + } + } + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + for (int index = 0; index < instructionVisitorCount; index++) + { + 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 000000000..a31a4082f --- /dev/null +++ b/src/proguard/classfile/instruction/visitor/package.html @@ -0,0 +1,3 @@ + +This package contains visitors for instructions. + diff --git a/src/proguard/classfile/io/LibraryClassReader.java b/src/proguard/classfile/io/LibraryClassReader.java new file mode 100644 index 000000000..d1eab3eee --- /dev/null +++ b/src/proguard/classfile/io/LibraryClassReader.java @@ -0,0 +1,383 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + dataInput.skipBytes(4); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + dataInput.skipBytes(3); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + dataInput.skipBytes(4); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.u2nameIndex = dataInput.readUnsignedShort(); + } + + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + dataInput.skipBytes(2); + } + + + 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_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_Utf8: return new Utf8Constant(); + case ClassConstants.CONSTANT_InvokeDynamic: return new InvokeDynamicConstant(); + case ClassConstants.CONSTANT_MethodHandle: return new MethodHandleConstant(); + 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_MethodType : return new MethodTypeConstant(); + 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 000000000..80a38f740 --- /dev/null +++ b/src/proguard/classfile/io/ProgramClassReader.java @@ -0,0 +1,919 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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, + BootstrapMethodInfoVisitor, + 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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + invokeDynamicConstant.u2bootstrapMethodAttributeIndex = dataInput.readUnsignedShort(); + invokeDynamicConstant.u2nameAndTypeIndex = dataInput.readUnsignedShort(); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + methodHandleConstant.u1referenceKind = dataInput.readUnsignedByte(); + methodHandleConstant.u2referenceIndex = dataInput.readUnsignedShort(); + } + + + 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 visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + methodTypeConstant.u2descriptorIndex = 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 visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + // Read the bootstrap methods. + bootstrapMethodsAttribute.u2bootstrapMethodsCount = dataInput.readUnsignedShort(); + + bootstrapMethodsAttribute.bootstrapMethods = new BootstrapMethodInfo[bootstrapMethodsAttribute.u2bootstrapMethodsCount]; + for (int index = 0; index < bootstrapMethodsAttribute.u2bootstrapMethodsCount; index++) + { + BootstrapMethodInfo bootstrapMethodInfo = new BootstrapMethodInfo(); + visitBootstrapMethodInfo(clazz, bootstrapMethodInfo); + bootstrapMethodsAttribute.bootstrapMethods[index] = bootstrapMethodInfo; + } + } + + + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + visitAnnotation(clazz, annotation); + annotationsAttribute.annotations[index] = annotation; + } + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Read the parameter annotations. + parameterAnnotationsAttribute.u2parametersCount = dataInput.readUnsignedByte(); + + // The java compilers of JDK 1.5, JDK 1.6, and Eclipse all count the + // number of parameters of constructors of non-static inner classes + // incorrectly. Fix it right here. + int parameterStart = 0; + if (method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + int realParametersCount = ClassUtil.internalMethodParameterCount(method.getDescriptor(clazz)); + parameterStart = realParametersCount - parameterAnnotationsAttribute.u2parametersCount; + parameterAnnotationsAttribute.u2parametersCount = realParametersCount; + } + + parameterAnnotationsAttribute.u2parameterAnnotationsCount = new int[parameterAnnotationsAttribute.u2parametersCount]; + parameterAnnotationsAttribute.parameterAnnotations = new Annotation[parameterAnnotationsAttribute.u2parametersCount][]; + + for (int parameterIndex = parameterStart; 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(); + 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 BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + bootstrapMethodInfo.u2methodHandleIndex = dataInput.readUnsignedShort(); + + // Read the bootstrap method arguments. + bootstrapMethodInfo.u2methodArgumentCount = dataInput.readUnsignedShort(); + bootstrapMethodInfo.u2methodArguments = new int[bootstrapMethodInfo.u2methodArgumentCount]; + for (int index = 0; index < bootstrapMethodInfo.u2methodArgumentCount; index++) + { + bootstrapMethodInfo.u2methodArguments[index] = dataInput.readUnsignedShort(); + } + } + + + // 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(); + 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_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_Utf8: return new Utf8Constant(); + case ClassConstants.CONSTANT_InvokeDynamic: return new InvokeDynamicConstant(); + case ClassConstants.CONSTANT_MethodHandle: return new MethodHandleConstant(); + 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_MethodType : return new MethodTypeConstant(); + 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_BootstrapMethods) ? (Attribute)new BootstrapMethodsAttribute(): + 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((char)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 000000000..dbf1de3f7 --- /dev/null +++ b/src/proguard/classfile/io/ProgramClassWriter.java @@ -0,0 +1,735 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 writing to 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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + dataOutput.writeShort(invokeDynamicConstant.u2bootstrapMethodAttributeIndex); + dataOutput.writeShort(invokeDynamicConstant.u2nameAndTypeIndex); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + dataOutput.writeByte(methodHandleConstant.u1referenceKind); + dataOutput.writeShort(methodHandleConstant.u2referenceIndex); + } + + + 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 visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + dataOutput.writeShort(methodTypeConstant.u2descriptorIndex); + } + + + 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, + BootstrapMethodInfoVisitor, + InnerClassesInfoVisitor, + ExceptionInfoVisitor, + StackMapFrameVisitor, + VerificationTypeVisitor, + LineNumberInfoVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor + { + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + // Write the unknown information. + dataOutput.write(unknownAttribute.info); + } + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + // Write the bootstrap methods. + dataOutput.writeShort(bootstrapMethodsAttribute.u2bootstrapMethodsCount); + + bootstrapMethodsAttribute.bootstrapMethodEntriesAccept(clazz, this); + } + + + 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++) + { + visitAnnotation(clazz, annotations[index]); + } + + } + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Write the default element value. + annotationDefaultAttribute.defaultValue.accept(clazz, null, this); + } + + + // Implementations for BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + dataOutput.writeShort(bootstrapMethodInfo.u2methodHandleIndex); + + // Write the bootstrap method arguments. + dataOutput.writeShort(bootstrapMethodInfo.u2methodArgumentCount); + + for (int index = 0; index < bootstrapMethodInfo.u2methodArgumentCount; index++) + { + dataOutput.writeShort(bootstrapMethodInfo.u2methodArguments[index]); + } + } + + + // 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 000000000..ce2d1a5e5 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..44acc701b --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..780b91710 --- /dev/null +++ b/src/proguard/classfile/io/package.html @@ -0,0 +1,3 @@ + +This package contains classes for reading and writing class files. + diff --git a/src/proguard/classfile/package.html b/src/proguard/classfile/package.html new file mode 100644 index 000000000..fad087cb8 --- /dev/null +++ b/src/proguard/classfile/package.html @@ -0,0 +1,15 @@ + +This package contains classes to represent the various elements of class files. +

+A class file is represented by the {@link proguard.classfile.ClassFile +ClassFile} interface. This interface currently has two alternative +representations: +

    +
  • {@link ProgramClassFile ProgramClassFile}: + a complete representation that can be read, modified, and written back. +
  • {@link LibraryClassFile LibraryClassFile}: + an incomplete representation that can be only be read. It is however + more compact than ProgramClassFile, and sufficient for + analyzing class files from library jars. +
+ diff --git a/src/proguard/classfile/util/AccessUtil.java b/src/proguard/classfile/util/AccessUtil.java new file mode 100644 index 000000000..d16f57651 --- /dev/null +++ b/src/proguard/classfile/util/AccessUtil.java @@ -0,0 +1,105 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + + +/** + * Utility methods for working with access flags. For convenience, this class + * defines access levels, in ascending order: PRIVATE, + * PACKAGE_VISIBLE, PROTECTED, and PUBLIC. + * + * @author Eric Lafortune + */ +public class AccessUtil +{ + public static final int PRIVATE = 0; + public static final int PACKAGE_VISIBLE = 1; + public static final int PROTECTED = 2; + public static final int PUBLIC = 3; + + + // The mask of access flags. + private static final int ACCESS_MASK = + ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_PROTECTED; + + + /** + * Returns the corresponding access level of the given access flags. + * @param accessFlags the internal access flags. + * @return the corresponding access level: PRIVATE, + * PACKAGE_VISIBLE, PROTECTED, or + * PUBLIC. + */ + public static int accessLevel(int accessFlags) + { + switch (accessFlags & ACCESS_MASK) + { + case ClassConstants.INTERNAL_ACC_PRIVATE: return PRIVATE; + default: return PACKAGE_VISIBLE; + case ClassConstants.INTERNAL_ACC_PROTECTED: return PROTECTED; + case ClassConstants.INTERNAL_ACC_PUBLIC: return PUBLIC; + } + } + + + /** + * Returns the corresponding access flags of the given access level. + * @param accessLevel the access level: PRIVATE, + * PACKAGE_VISIBLE, PROTECTED, + * or PUBLIC. + * @return the corresponding internal access flags, the internal access + * flags as a logical bit mask of INTERNAL_ACC_PRIVATE, + * INTERNAL_ACC_PROTECTED, and + * INTERNAL_ACC_PUBLIC. + */ + public static int accessFlags(int accessLevel) + { + 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; + } + } + + + /** + * Replaces the access part of the given access flags. + * @param accessFlags the internal access flags. + * @param accessFlags the new internal access flags. + */ + 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; + } + + return (accessFlags & ~ACCESS_MASK) | + (newAccessFlags & ACCESS_MASK); + } +} diff --git a/src/proguard/classfile/util/ClassReferenceInitializer.java b/src/proguard/classfile/util/ClassReferenceInitializer.java new file mode 100644 index 000000000..431d54afc --- /dev/null +++ b/src/proguard/classfile/util/ClassReferenceInitializer.java @@ -0,0 +1,559 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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. + *

+ * 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. + *

+ * All field and method reference constant pool entries get direct references + * to the corresponding classes, fields, and methods. + *

+ * All name and type constant pool entries get a list of direct references to + * the classes listed in the type. + *

+ * This visitor optionally prints warnings if some items can't be found. + *

+ * 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 missingClassWarningPrinter; + private final WarningPrinter missingMemberWarningPrinter; + private final WarningPrinter dependencyWarningPrinter; + + 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 + * or class members can't be found or if they are in the program class pool. + */ + public ClassReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter missingClassWarningPrinter, + WarningPrinter missingMemberWarningPrinter, + WarningPrinter dependencyWarningPrinter) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingClassWarningPrinter = missingClassWarningPrinter; + this.missingMemberWarningPrinter = missingMemberWarningPrinter; + this.dependencyWarningPrinter = dependencyWarningPrinter; + } + + + // 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(programClass.getName(), + programField.getDescriptor(programClass)); + + // Initialize the attributes. + programField.attributesAccept(programClass, this); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + programMethod.referencedClasses = + findReferencedClasses(programClass.getName(), + programMethod.getDescriptor(programClass)); + + // Initialize the attributes. + programMethod.attributesAccept(programClass, this); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + libraryField.referencedClass = + findReferencedClass(libraryClass.getName(), + libraryField.getDescriptor(libraryClass)); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + libraryMethod.referencedClasses = + findReferencedClasses(libraryClass.getName(), + 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(clazz.getName(), ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + // Fill out the MethodHandle class. + methodHandleConstant.javaLangInvokeMethodHandleClass = + findClass(clazz.getName(), ClassConstants.INTERNAL_NAME_JAVA_LANG_INVOKE_METHOD_HANDLE); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + String className = refConstant.getClassName(clazz); + + // Methods for array types should be found in the Object class. + if (ClassUtil.isInternalArrayType(className)) + { + className = ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT; + } + + // 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(clazz.getName(), className); + + if (referencedClass != null) + { + 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) + { + // We haven't found the class member anywhere in the hierarchy. + missingMemberWarningPrinter.print(clazz.getName(), + className, + "Warning: " + + ClassUtil.externalClassName(clazz.getName()) + + ": can't find referenced " + + (isFieldRef ? + "field '" + ClassUtil.externalFullFieldDescription(0, name, type) : + "method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) + + "' in class " + + ClassUtil.externalClassName(className)); + } + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + String className = clazz.getName(); + + // Fill out the referenced class. + classConstant.referencedClass = + findClass(className, ClassUtil.internalClassNameFromClassType(classConstant.getName(clazz))); + + // Fill out the Class class. + classConstant.javaLangClassClass = + findClass(className, ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS); + } + + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + // Fill out the MethodType class. + methodTypeConstant.javaLangInvokeMethodTypeClass = + findClass(clazz.getName(), ClassConstants.INTERNAL_NAME_JAVA_LANG_INVOKE_METHOD_TYPE); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + String className = clazz.getName(); + String enclosingClassName = enclosingMethodAttribute.getClassName(clazz); + + // See if we can find the referenced class. + enclosingMethodAttribute.referencedClass = + findClass(className, enclosingClassName); + + if (enclosingMethodAttribute.referencedClass != null) + { + // Is there an enclosing method? Otherwise it's just initialization + // code outside of the constructors. + if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) + { + String name = enclosingMethodAttribute.getName(clazz); + String type = enclosingMethodAttribute.getType(clazz); + + // See if we can find the method in the referenced class. + enclosingMethodAttribute.referencedMethod = + enclosingMethodAttribute.referencedClass.findMethod(name, type); + + if (enclosingMethodAttribute.referencedMethod == null) + { + // We couldn't find the enclosing method. + missingMemberWarningPrinter.print(className, + enclosingClassName, + "Warning: " + + ClassUtil.externalClassName(className) + + ": can't find enclosing method '" + + ClassUtil.externalFullMethodDescription(enclosingClassName, 0, name, type) + + "' in class " + + ClassUtil.externalClassName(enclosingClassName)); + } + } + } + } + + + 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.getName(), + 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.getName(), + clazz.getString(localVariableInfo.u2descriptorIndex)); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + localVariableTypeInfo.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(localVariableTypeInfo.u2signatureIndex)); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + annotation.referencedClasses = + findReferencedClasses(clazz.getName(), + 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.getName(), + clazz.getString(enumConstantElementValue.u2typeNameIndex)); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + initializeElementValue(clazz, annotation, classElementValue); + + classElementValue.referencedClasses = + findReferencedClasses(clazz.getName(), + 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 + * null if there isn't any useful reference. + */ + private Clazz findReferencedClass(String referencingClassName, + String descriptor) + { + DescriptorClassEnumeration enumeration = + new DescriptorClassEnumeration(descriptor); + + enumeration.nextFluff(); + + if (enumeration.hasMoreClassNames()) + { + return findClass(referencingClassName, enumeration.nextClassName()); + } + + return null; + } + + + /** + * Returns an array of classes referenced by the given descriptor, or + * null if there aren't any useful references. + */ + private Clazz[] findReferencedClasses(String referencingClassName, + 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 fluff = enumeration.nextFluff(); + String name = enumeration.nextClassName(); + + Clazz referencedClass = findClass(referencingClassName, 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 null if it can't be found. + */ + private Clazz findClass(String referencingClassName, String name) + { + // Is it an array type? + if (ClassUtil.isInternalArrayType(name)) + { + // Ignore any primitive array types. + if (!ClassUtil.isInternalClassType(name)) + { + return null; + } + + // Strip the array part. + name = ClassUtil.internalClassNameFromClassType(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 && + missingClassWarningPrinter != null) + { + // We didn't find the superclass or interface. Print a warning. + missingClassWarningPrinter.print(referencingClassName, + name, + "Warning: " + + ClassUtil.externalClassName(referencingClassName) + + ": can't find referenced class " + + ClassUtil.externalClassName(name)); + } + } + else if (dependencyWarningPrinter != null) + { + // The superclass or interface was found in the program class pool. + // Print a warning. + dependencyWarningPrinter.print(referencingClassName, + name, + "Warning: library class " + + ClassUtil.externalClassName(referencingClassName) + + " depends on program class " + + ClassUtil.externalClassName(name)); + } + + return clazz; + } +} diff --git a/src/proguard/classfile/util/ClassSubHierarchyInitializer.java b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java new file mode 100644 index 000000000..993a55989 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..fb431b86a --- /dev/null +++ b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java @@ -0,0 +1,163 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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. + *

+ * 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. + *

+ * This visitor optionally prints warnings if some superclasses can't be found + * or if they are in the program class pool. + * + * @author Eric Lafortune + */ +public class ClassSuperHierarchyInitializer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor +{ + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter missingWarningPrinter; + private final WarningPrinter dependencyWarningPrinter; + + + /** + * Creates a new ClassSuperHierarchyInitializer that initializes the super + * hierarchy of all visited class files, optionally printing warnings if + * some classes can't be found or if they are in the program class pool. + */ + public ClassSuperHierarchyInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter missingWarningPrinter, + WarningPrinter dependencyWarningPrinter) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingWarningPrinter = missingWarningPrinter; + this.dependencyWarningPrinter = dependencyWarningPrinter; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Link to the super class. + programClass.superClassConstantAccept(this); + + // Link to the interfaces. + programClass.interfaceConstantsAccept(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; + } + } + + + // 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 null if it can't be found. + */ + private Clazz findClass(String referencingClassName, 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 && + missingWarningPrinter != null) + { + // We didn't find the superclass or interface. Print a warning. + missingWarningPrinter.print(referencingClassName, + name, + "Warning: " + + ClassUtil.externalClassName(referencingClassName) + + ": can't find superclass or interface " + + ClassUtil.externalClassName(name)); + } + } + else if (dependencyWarningPrinter != null) + { + // The superclass or interface was found in the program class pool. + // Print a warning. + dependencyWarningPrinter.print(referencingClassName, + name, + "Warning: library class " + + ClassUtil.externalClassName(referencingClassName) + + " extends or implements program class " + + ClassUtil.externalClassName(name)); + } + + return clazz; + } +} diff --git a/src/proguard/classfile/util/ClassUtil.java b/src/proguard/classfile/util/ClassUtil.java new file mode 100644 index 000000000..fb38616bc --- /dev/null +++ b/src/proguard/classfile/util/ClassUtil.java @@ -0,0 +1,1227 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +import java.util.List; + +/** + * Utility methods for converting between internal and external representations + * of names and descriptions. + * + * @author Eric Lafortune + */ +public class ClassUtil +{ + private static final String EMPTY_STRING = ""; + + + /** + * Checks whether the given class magic number is correct. + * @param magicNumber the magic number. + * @throws UnsupportedOperationException when the magic number is incorrect. + */ + public static void checkMagicNumber(int magicNumber) throws UnsupportedOperationException + { + if (magicNumber != ClassConstants.MAGIC) + { + 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 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_7_ALIAS) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_7) ? ClassConstants.INTERNAL_CLASS_VERSION_1_7 : + 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; + case ClassConstants.INTERNAL_CLASS_VERSION_1_7: return ClassConstants.EXTERNAL_CLASS_VERSION_1_7; + default: return null; + } + } + + + /** + * 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 classVersion) throws UnsupportedOperationException + { + if (classVersion < ClassConstants.INTERNAL_CLASS_VERSION_1_0 || + classVersion > ClassConstants.INTERNAL_CLASS_VERSION_1_7) + { + throw new UnsupportedOperationException("Unsupported class version number ["+ + internalMajorClassVersion(classVersion)+"."+ + internalMinorClassVersion(classVersion)+"] (maximum "+ + ClassConstants.INTERNAL_CLASS_VERSION_1_7_MAJOR+"."+ + ClassConstants.INTERNAL_CLASS_VERSION_1_7_MINOR+", Java "+ + ClassConstants.EXTERNAL_CLASS_VERSION_1_7+")"); + } + } + + + /** + * Converts an external class name into an internal class name. + * @param externalClassName the external class name, + * e.g. "java.lang.Object" + * @return the internal class name, + * e.g. "java/lang/Object". + */ + public static String internalClassName(String externalClassName) + { + return externalClassName.replace(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, + ClassConstants.INTERNAL_PACKAGE_SEPARATOR); + } + + + /** + * Converts an internal class description into an external class description. + * @param accessFlags the access flags of the class. + * @param internalClassName the internal class name, + * e.g. "java/lang/Object". + * @return the external class description, + * e.g. "public java.lang.Object". + */ + public static String externalFullClassDescription(int accessFlags, + String internalClassName) + { + return externalClassAccessFlags(accessFlags) + + externalClassName(internalClassName); + } + + + /** + * Converts an internal class name into an external class name. + * @param internalClassName the internal class name, + * e.g. "java/lang/Object". + * @return the external class name, + * e.g. "java.lang.Object". + */ + public static String externalClassName(String internalClassName) + { + return //internalClassName.startsWith(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG) && + //internalClassName.indexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length() + 1) < 0 ? + //internalClassName.substring(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length()) : + internalClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); + } + + + /** + * Returns the external base type of an external array type, dropping any + * array brackets. + * @param externalArrayType the external array type, + * e.g. "java.lang.Object[][]" + * @return the external base type, + * e.g. "java.lang.Object". + */ + public static String externalBaseType(String externalArrayType) + { + int index = externalArrayType.indexOf(ClassConstants.EXTERNAL_TYPE_ARRAY); + return index >= 0 ? + externalArrayType.substring(0, index) : + externalArrayType; + } + + + /** + * Returns the external short class name of an external class name, dropping + * the package specification. + * @param externalClassName the external class name, + * e.g. "java.lang.Object" + * @return the external short class name, + * e.g. "Object". + */ + public static String externalShortClassName(String externalClassName) + { + int index = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); + return externalClassName.substring(index+1); + } + + + /** + * Returns whether the given internal type is an array type. + * @param internalType the internal type, + * e.g. "[[Ljava/lang/Object;". + * @return true if the given type is an array type, + * false otherwise. + */ + public static boolean isInternalArrayType(String internalType) + { + return internalType.length() > 1 && + internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY; + } + + + /** + * Returns the number of dimensions of the given internal type. + * @param internalType the internal type, + * e.g. "[[Ljava/lang/Object;". + * @return the number of dimensions, e.g. 2. + */ + public static int internalArrayTypeDimensionCount(String internalType) + { + int dimensions = 0; + while (internalType.charAt(dimensions) == ClassConstants.INTERNAL_TYPE_ARRAY) + { + dimensions++; + } + + return dimensions; + } + + + /** + * Returns whether the given internal class name is one of the interfaces + * that is implemented by all array types. These class names are + * "java/lang/Object", "java/lang/Cloneable", and + * "java/io/Serializable" + * @param internalClassName the internal class name, + * e.g. "java/lang/Object". + * @return true if the given type is an array interface name, + * false 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, + * e.g. "I". + * @return true if the given type is a class type, + * false otherwise. + */ + public static boolean isInternalPrimitiveType(char internalType) + { + return internalType == ClassConstants.INTERNAL_TYPE_BOOLEAN || + internalType == ClassConstants.INTERNAL_TYPE_BYTE || + internalType == ClassConstants.INTERNAL_TYPE_CHAR || + internalType == ClassConstants.INTERNAL_TYPE_SHORT || + internalType == ClassConstants.INTERNAL_TYPE_INT || + internalType == ClassConstants.INTERNAL_TYPE_FLOAT || + internalType == ClassConstants.INTERNAL_TYPE_LONG || + internalType == ClassConstants.INTERNAL_TYPE_DOUBLE; + } + + + /** + * Returns whether the given internal type is a primitive Category 2 type. + * @param internalType the internal type, + * e.g. "L". + * @return true if the given type is a Category 2 type, + * false otherwise. + */ + public static boolean isInternalCategory2Type(String internalType) + { + return internalType.length() == 1 && + (internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_LONG || + internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_DOUBLE); + } + + + /** + * Returns whether the given internal type is a plain class type + * (including an array type of a plain class type). + * @param internalType the internal type, + * e.g. "Ljava/lang/Object;". + * @return true if the given type is a class type, + * false otherwise. + */ + public static boolean isInternalClassType(String internalType) + { + int length = internalType.length(); + return length > 1 && +// internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_CLASS_START && + internalType.charAt(length-1) == ClassConstants.INTERNAL_TYPE_CLASS_END; + } + + + /** + * Returns the internal type of a given class name. + * @param internalClassName the internal class name, + * e.g. "java/lang/Object". + * @return the internal type, + * e.g. "Ljava/lang/Object;". + */ + 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. "java/lang/Object". + * @param dimensionCount the number of array dimensions. + * @return the internal array type of the array elements, + * e.g. "Ljava/lang/Object;". + */ + 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. "[[Ljava/lang/Object;" or + * "[I". + * @return the internal type of the array elements, + * e.g. "Ljava/lang/Object;" or + * "I". + */ + public static String internalTypeFromArrayType(String internalArrayType) + { + int index = internalArrayType.lastIndexOf(ClassConstants.INTERNAL_TYPE_ARRAY); + return internalArrayType.substring(index+1); + } + + + /** + * Returns the internal class name of a given internal class type + * (including an array type). Types involving primitive types are returned + * unchanged. + * @param internalClassType the internal class type, + * e.g. "[Ljava/lang/Object;", + * "Ljava/lang/Object;", or + * "java/lang/Object". + * @return the internal class name, + * e.g. "java/lang/Object". + */ + public static String internalClassNameFromClassType(String internalClassType) + { + return isInternalClassType(internalClassType) ? + internalClassType.substring(internalClassType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1, + internalClassType.length()-1) : + internalClassType; + } + + + /** + * Returns the internal class name of any given internal descriptor type, + * disregarding array prefixes. + * @param internalClassType the internal class type, + * e.g. "Ljava/lang/Object;" or + * "[[I". + * @return the internal class name, + * e.g. "java/lang/Object" or + * null. + */ + public static String internalClassNameFromType(String internalClassType) + { + if (!isInternalClassType(internalClassType)) + { + return null; + } + + // Is it an array type? + if (isInternalArrayType(internalClassType)) + { + internalClassType = internalTypeFromArrayType(internalClassType); + } + + return internalClassNameFromClassType(internalClassType); + } + + + /** + * Returns whether the given method name refers to a class initializer or + * an instance initializer. + * @param internalMethodName the internal method name, + * e.g. "<clinit>". + * @return whether the method name refers to an initializer, + * e.g. true. + */ + public static boolean isInitializer(String internalMethodName) + { + return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT); + } + + + /** + * Returns the internal type of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(II)Z". + * @return the internal return type, + * e.g. "Z". + */ + public static String internalMethodReturnType(String internalMethodDescriptor) + { + int index = internalMethodDescriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + return internalMethodDescriptor.substring(index + 1); + } + + + /** + * Returns the number of parameters of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(ID)Z". + * @return the number of parameters, + * e.g. 2. + */ + public static int internalMethodParameterCount(String internalMethodDescriptor) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + int counter = 0; + while (internalTypeEnumeration.hasMoreTypes()) + { + internalTypeEnumeration.nextType(); + + counter++; + } + + return counter; + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two entries. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(ID)Z". + * @return the size taken up on the stack, + * e.g. 3. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor) + { + return internalMethodParameterSize(internalMethodDescriptor, true); + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two entries, and a non-static method taking up an additional + * entry. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(ID)Z". + * @param accessFlags the access flags of the method, + * e.g. 0. + * @return the size taken up on the stack, + * e.g. 4. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor, + int accessFlags) + { + return internalMethodParameterSize(internalMethodDescriptor, + (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0); + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two spaces, and a non-static method taking up an additional + * entry. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(ID)Z". + * @param isStatic specifies whether the method is static, + * e.g. false. + * @return the size taken up on the stack, + * e.g. 4. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor, + boolean isStatic) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + int size = isStatic ? 0 : 1; + while (internalTypeEnumeration.hasMoreTypes()) + { + String internalType = internalTypeEnumeration.nextType(); + + size += internalTypeSize(internalType); + } + + return size; + } + + + /** + * Returns the size taken up on the stack by the given internal type. + * The size is 1, except for long and double types, for which it is 2, + * and for the void type, for which 0 is returned. + * @param internalType the internal type, + * e.g. "I". + * @return the size taken up on the stack, + * e.g. 1. + */ + public static int internalTypeSize(String internalType) + { + if (internalType.length() == 1) + { + char internalPrimitiveType = internalType.charAt(0); + if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_LONG || + internalPrimitiveType == ClassConstants.INTERNAL_TYPE_DOUBLE) + { + return 2; + } + else if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_VOID) + { + return 0; + } + } + + return 1; + } + + + /** + * Converts an external type into an internal type. + * @param externalType the external type, + * e.g. "java.lang.Object[][]" or + * "int[]". + * @return the internal type, + * e.g. "[[Ljava/lang/Object;" or + * "[I". + */ + public static String internalType(String externalType) + { + // Strip the array part, if any. + int dimensionCount = externalArrayTypeDimensionCount(externalType); + if (dimensionCount > 0) + { + externalType = externalType.substring(0, externalType.length() - dimensionCount * ClassConstants.EXTERNAL_TYPE_ARRAY.length()); + } + + // Analyze the actual type part. + char internalTypeChar = + externalType.equals(ClassConstants.EXTERNAL_TYPE_VOID ) ? + ClassConstants.INTERNAL_TYPE_VOID : + externalType.equals(ClassConstants.EXTERNAL_TYPE_BOOLEAN) ? + ClassConstants.INTERNAL_TYPE_BOOLEAN : + externalType.equals(ClassConstants.EXTERNAL_TYPE_BYTE ) ? + ClassConstants.INTERNAL_TYPE_BYTE : + externalType.equals(ClassConstants.EXTERNAL_TYPE_CHAR ) ? + ClassConstants.INTERNAL_TYPE_CHAR : + externalType.equals(ClassConstants.EXTERNAL_TYPE_SHORT ) ? + ClassConstants.INTERNAL_TYPE_SHORT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_INT ) ? + ClassConstants.INTERNAL_TYPE_INT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_FLOAT ) ? + ClassConstants.INTERNAL_TYPE_FLOAT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_LONG ) ? + ClassConstants.INTERNAL_TYPE_LONG : + externalType.equals(ClassConstants.EXTERNAL_TYPE_DOUBLE ) ? + ClassConstants.INTERNAL_TYPE_DOUBLE : + externalType.equals("%" ) ? + '%' : + (char)0; + + String internalType = + 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++) + { + internalType = ClassConstants.INTERNAL_TYPE_ARRAY + internalType; + } + + return internalType; + } + + + /** + * Returns the number of dimensions of the given external type. + * @param externalType the external type, + * e.g. "[[Ljava/lang/Object;". + * @return the number of dimensions, e.g. 2. + */ + public static int externalArrayTypeDimensionCount(String externalType) + { + int dimensions = 0; + int length = ClassConstants.EXTERNAL_TYPE_ARRAY.length(); + int offset = externalType.length() - length; + while (externalType.regionMatches(offset, + ClassConstants.EXTERNAL_TYPE_ARRAY, + 0, + length)) + { + dimensions++; + offset -= length; + } + + return dimensions; + } + + + /** + * Converts an internal type into an external type. + * @param internalType the internal type, + * e.g. "[[Ljava/lang/Object;" or + * "[I". + * @return the external type, + * e.g. "java.lang.Object[][]" or + * "int[]". + */ + public static String externalType(String internalType) + { + // Strip the array part, if any. + int dimensionCount = internalArrayTypeDimensionCount(internalType); + if (dimensionCount > 0) + { + internalType = internalType.substring(dimensionCount); + } + + // Analyze the actual type part. + char internalTypeChar = internalType.charAt(0); + + String externalType = + internalTypeChar == ClassConstants.INTERNAL_TYPE_VOID ? + ClassConstants.EXTERNAL_TYPE_VOID : + internalTypeChar == ClassConstants.INTERNAL_TYPE_BOOLEAN ? + ClassConstants.EXTERNAL_TYPE_BOOLEAN : + internalTypeChar == ClassConstants.INTERNAL_TYPE_BYTE ? + ClassConstants.EXTERNAL_TYPE_BYTE : + internalTypeChar == ClassConstants.INTERNAL_TYPE_CHAR ? + ClassConstants.EXTERNAL_TYPE_CHAR : + internalTypeChar == ClassConstants.INTERNAL_TYPE_SHORT ? + ClassConstants.EXTERNAL_TYPE_SHORT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_INT ? + ClassConstants.EXTERNAL_TYPE_INT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_FLOAT ? + ClassConstants.EXTERNAL_TYPE_FLOAT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_LONG ? + ClassConstants.EXTERNAL_TYPE_LONG : + internalTypeChar == ClassConstants.INTERNAL_TYPE_DOUBLE ? + ClassConstants.EXTERNAL_TYPE_DOUBLE : + internalTypeChar == '%' ? + "%" : + internalTypeChar == ClassConstants.INTERNAL_TYPE_CLASS_START ? + externalClassName(internalType.substring(1, internalType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END))) : + null; + + if (externalType == null) + { + throw new IllegalArgumentException("Unknown type ["+internalType+"]"); + } + + // Append the array part, if any. + for (int count = 0; count < dimensionCount; count++) + { + externalType += ClassConstants.EXTERNAL_TYPE_ARRAY; + } + + return externalType; + } + + + /** + * Returns whether the given internal descriptor String represents a method + * descriptor. + * @param internalDescriptor the internal descriptor String, + * e.g. "(II)Z". + * @return true if the given String is a method descriptor, + * false otherwise. + */ + public static boolean isInternalMethodDescriptor(String internalDescriptor) + { + return internalDescriptor.charAt(0) == ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN; + } + + + /** + * Returns whether the given member String represents an external method + * name with arguments. + * @param externalMemberNameAndArguments the external member String, + * e.g. "myField" or + * e.g. "myMethod(int,int)". + * @return true if the given String refers to a method, + * false otherwise. + */ + public static boolean isExternalMethodNameAndArguments(String externalMemberNameAndArguments) + { + return externalMemberNameAndArguments.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) > 0; + } + + + /** + * Returns the name part of the given external method name and arguments. + * @param externalMethodNameAndArguments the external method name and arguments, + * e.g. "myMethod(int,int)". + * @return the name part of the String, e.g. "myMethod". + */ + public static String externalMethodName(String externalMethodNameAndArguments) + { + ExternalTypeEnumeration externalTypeEnumeration = + new ExternalTypeEnumeration(externalMethodNameAndArguments); + + return externalTypeEnumeration.methodName(); + } + + + /** + * Converts the given external method return type and name and arguments to + * an internal method descriptor. + * @param externalReturnType the external method return type, + * e.g. "boolean". + * @param externalMethodNameAndArguments the external method name and arguments, + * e.g. "myMethod(int,int)". + * @return the internal method descriptor, + * e.g. "(II)Z". + */ + public static String internalMethodDescriptor(String externalReturnType, + String externalMethodNameAndArguments) + { + StringBuffer internalMethodDescriptor = new StringBuffer(); + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + ExternalTypeEnumeration externalTypeEnumeration = + new ExternalTypeEnumeration(externalMethodNameAndArguments); + + while (externalTypeEnumeration.hasMoreTypes()) + { + internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType())); + } + + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalType(externalReturnType)); + + return internalMethodDescriptor.toString(); + } + + + /** + * Converts the given external method return type and List of arguments to + * an internal method descriptor. + * @param externalReturnType the external method return type, + * e.g. "boolean". + * @param externalArguments the external method arguments, + * e.g. { "int", "int" }. + * @return the internal method descriptor, + * e.g. "(II)Z". + */ + public static String internalMethodDescriptor(String externalReturnType, + List externalArguments) + { + StringBuffer internalMethodDescriptor = new StringBuffer(); + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + for (int index = 0; index < externalArguments.size(); index++) + { + internalMethodDescriptor.append(internalType((String)externalArguments.get(index))); + } + + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalType(externalReturnType)); + + return internalMethodDescriptor.toString(); + } + + + /** + * Converts an internal field description into an external full field description. + * @param accessFlags the access flags of the field. + * @param fieldName the field name, + * e.g. "myField". + * @param internalFieldDescriptor the internal field descriptor, + * e.g. "Z". + * @return the external full field description, + * e.g. "public boolean myField". + */ + public static String externalFullFieldDescription(int accessFlags, + String fieldName, + String internalFieldDescriptor) + { + return externalFieldAccessFlags(accessFlags) + + externalType(internalFieldDescriptor) + + ' ' + + fieldName; + } + + + /** + * Converts an internal method description into an external full method description. + * @param internalClassName the internal name of the class of the method, + * e.g. "mypackage/MyClass". + * @param accessFlags the access flags of the method. + * @param internalMethodName the internal method name, + * e.g. "myMethod" or + * "<init>". + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(II)Z". + * @return the external full method description, + * e.g. "public boolean myMethod(int,int)" or + * "public MyClass(int,int)". + */ + public static String externalFullMethodDescription(String internalClassName, + int accessFlags, + String internalMethodName, + String internalMethodDescriptor) + { + return externalMethodAccessFlags(accessFlags) + + externalMethodReturnTypeAndName(internalClassName, + internalMethodName, + internalMethodDescriptor) + + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN + + externalMethodArguments(internalMethodDescriptor) + + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE; + } + + + /** + * Converts internal class access flags into an external access description. + * @param accessFlags the class access flags. + * @return the external class access description, + * e.g. "public final ". + */ + public static String externalClassAccessFlags(int accessFlags) + { + return externalClassAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal class access flags into an external access description. + * @param accessFlags the class access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external class access description, + * e.g. "public final ". + */ + public static String externalClassAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) + { + // Only in InnerClasses attributes. + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + // Only in InnerClasses attributes. + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + // Only in InnerClasses attributes. + 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(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ANNOTATION); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ENUM).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNTHETIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNTHETIC).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal field access flags into an external access description. + * @param accessFlags the field access flags. + * @return the external field access description, + * e.g. "public volatile ". + */ + public static String externalFieldAccessFlags(int accessFlags) + { + return externalFieldAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal field access flags into an external access description. + * @param accessFlags the field access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external field access description, + * e.g. "public volatile ". + */ + public static String externalFieldAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + 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(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + 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(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + 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(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_TRANSIENT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNTHETIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNTHETIC).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal method access flags into an external access description. + * @param accessFlags the method access flags. + * @return the external method access description, + * e.g. "public synchronized ". + */ + public static String externalMethodAccessFlags(int accessFlags) + { + return externalMethodAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal method access flags into an external access description. + * @param accessFlags the method access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external method access description, + * e.g. "public synchronized ". + */ + public static String externalMethodAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + 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(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + 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(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + 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(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_BRIDGE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_BRIDGE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_VARARGS) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VARARGS).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) + { + 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(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STRICT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNTHETIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNTHETIC).append(' '); + } + + return string.toString(); + } + + + /** + * Converts an internal method descriptor into an external method return type. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(II)Z". + * @return the external method return type, + * e.g. "boolean". + */ + public static String externalMethodReturnType(String internalMethodDescriptor) + { + return externalType(internalMethodReturnType(internalMethodDescriptor)); + } + + + /** + * Converts an internal class name, method name, and method descriptor to + * an external method return type and name. + * @param internalClassName the internal name of the class of the method, + * e.g. "mypackage/MyClass". + * @param internalMethodName the internal method name, + * e.g. "myMethod" or + * "<init>". + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(II)Z". + * @return the external method return type and name, + * e.g. "boolean myMethod" or + * "MyClass". + */ + private static String externalMethodReturnTypeAndName(String internalClassName, + String internalMethodName, + String internalMethodDescriptor) + { + return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? + externalShortClassName(externalClassName(internalClassName)) : + (externalMethodReturnType(internalMethodDescriptor) + + ' ' + + internalMethodName); + } + + + /** + * Converts an internal method descriptor into an external method argument + * description. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(II)Z". + * @return the external method argument description, + * e.g. "int,int". + */ + public static String externalMethodArguments(String internalMethodDescriptor) + { + StringBuffer externalMethodNameAndArguments = new StringBuffer(); + + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + while (internalTypeEnumeration.hasMoreTypes()) + { + externalMethodNameAndArguments.append(externalType(internalTypeEnumeration.nextType())); + if (internalTypeEnumeration.hasMoreTypes()) + { + externalMethodNameAndArguments.append(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR); + } + } + + return externalMethodNameAndArguments.toString(); + } + + + /** + * Returns the internal package name of the given internal class name. + * @param internalClassName the internal class name, + * e.g. "java/lang/Object". + * @return the internal package name, + * e.g. "java/lang". + */ + public static String internalPackageName(String internalClassName) + { + String internalPackagePrefix = internalPackagePrefix(internalClassName); + int length = internalPackagePrefix.length(); + return length > 0 ? + internalPackagePrefix.substring(0, length - 1) : + ""; + } + + + /** + * Returns the internal package prefix of the given internal class name. + * @param internalClassName the internal class name, + * e.g. "java/lang/Object". + * @return the internal package prefix, + * e.g. "java/lang/". + */ + public static String internalPackagePrefix(String internalClassName) + { + return internalClassName.substring(0, internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + internalClassName.length() - 2) + 1); + } + + + /** + * Returns the external package name of the given external class name. + * @param externalClassName the external class name, + * e.g. "java.lang.Object". + * @return the external package name, + * e.g. "java.lang". + */ + public static String externalPackageName(String externalClassName) + { + String externalPackagePrefix = externalPackagePrefix(externalClassName); + int length = externalPackagePrefix.length(); + return length > 0 ? + externalPackagePrefix.substring(0, length - 1) : + ""; + } + + + /** + * Returns the external package prefix of the given external class name. + * @param externalClassName the external class name, + * e.g. "java.lang.Object". + * @return the external package prefix, + * e.g. "java.lang.". + */ + 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 new file mode 100644 index 000000000..81590faae --- /dev/null +++ b/src/proguard/classfile/util/DescriptorClassEnumeration.java @@ -0,0 +1,236 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +import java.util.Stack; + +/** + * A DescriptorClassEnumeration provides an enumeration of all + * classes mentioned in a given descriptor or signature. + * + * @author Eric Lafortune + */ +public class DescriptorClassEnumeration +{ + private String descriptor; + + private int index; + private int nestingLevel; + private boolean isInnerClassName; + private String accumulatedClassName; + private Stack accumulatedClassNames; + + + /** + * Creates a new DescriptorClassEnumeration for the given descriptor. + */ + public DescriptorClassEnumeration(String descriptor) + { + this.descriptor = descriptor; + } + + + /** + * Returns the number of classes contained in the descriptor. This + * is the number of class names that the enumeration will return. + */ + public int classCount() + { + int count = 0; + + nextFluff(); + while (hasMoreClassNames()) + { + count++; + + nextClassName(); + nextFluff(); + } + + index = 0; + + return count; + } + + + /** + * Returns whether the enumeration can provide more class names from the + * descriptor. + */ + public boolean hasMoreClassNames() + { + return index < descriptor.length(); + } + + + /** + * Returns the next fluff (surrounding class names) from the descriptor. + */ + public String nextFluff() + { + int fluffStartIndex = index; + + // Find the first token marking the start of a class name 'L' or '.'. + loop: while (index < descriptor.length()) + { + switch (descriptor.charAt(index++)) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + { + nestingLevel++; + + // Make sure we have a stack. + if (accumulatedClassNames == null) + { + accumulatedClassNames = new Stack(); + } + + // Remember the accumulated class name. + accumulatedClassNames.push(accumulatedClassName); + + break; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_END: + { + nestingLevel--; + + // Return to the accumulated class name outside the + // generic block. + accumulatedClassName = (String)accumulatedClassNames.pop(); + + continue loop; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_BOUND: + { + continue loop; + } + case ClassConstants.INTERNAL_TYPE_CLASS_START: + { + // We've found the start of an ordinary class name. + nestingLevel += 2; + isInnerClassName = false; + break loop; + } + case ClassConstants.INTERNAL_TYPE_CLASS_END: + { + nestingLevel -= 2; + break; + } + case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: + { + // We've found the start of an inner class name in a signature. + isInnerClassName = true; + break loop; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START: + { + // We've found the start of a type identifier. Skip to the end. + while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_CLASS_END); + break; + } + } + + if (nestingLevel == 1 && + descriptor.charAt(index) != ClassConstants.INTERNAL_TYPE_GENERIC_END) + { + // We're at the start of a type parameter. Skip to the start + // of the bounds. + while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_GENERIC_BOUND); + } + } + + return descriptor.substring(fluffStartIndex, index); + } + + + /** + * Returns the next class name from the descriptor. + */ + public String nextClassName() + { + int classNameStartIndex = index; + + // Find the first token marking the end of a class name '<' or ';'. + loop: while (true) + { + switch (descriptor.charAt(index)) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + case ClassConstants.INTERNAL_TYPE_CLASS_END: + case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: + { + break loop; + } + } + + index++; + } + + String className = descriptor.substring(classNameStartIndex, index); + + // Recompose the inner class name if necessary. + accumulatedClassName = isInnerClassName ? + accumulatedClassName + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR + className : + className; + + return accumulatedClassName; + } + + + /** + * Returns whether the most recently returned class name was a recomposed + * inner class name from a signature. + */ + public boolean isInnerClassName() + { + return isInnerClassName; + } + + + /** + * A main method for testing the class name enumeration. + */ + public static void main(String[] args) + { + try + { + for (int index = 0; index < args.length; index++) + { + String descriptor = args[index]; + + System.out.println("Descriptor ["+descriptor+"]"); + DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(descriptor); + System.out.println(" Fluff: ["+enumeration.nextFluff()+"]"); + while (enumeration.hasMoreClassNames()) + { + System.out.println(" Name: ["+enumeration.nextClassName()+"]"); + System.out.println(" Fluff: ["+enumeration.nextFluff()+"]"); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/classfile/util/DynamicClassReferenceInitializer.java b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java new file mode 100644 index 000000000..865e094ef --- /dev/null +++ b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java @@ -0,0 +1,485 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 Class.forName or + * .class 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. + *

+ * It optionally prints notes if on usage of + * (SomeClass)Class.forName(variable).newInstance(). + *

+ * 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 missingNotePrinter; + private final WarningPrinter dependencyWarningPrinter; + 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 + * warnings and notes, with optional class specifications for which never + * to print notes. + */ + public DynamicClassReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter missingNotePrinter, + WarningPrinter dependencyWarningPrinter, + WarningPrinter notePrinter, + StringMatcher noteExceptionMatcher) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingNotePrinter = missingNotePrinter; + this.dependencyWarningPrinter = dependencyWarningPrinter; + 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( + ClassUtil.externalBaseType(externalClassName)); + + stringConstant.referencedClass = findClass(clazz.getName(), 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 (noteExceptionMatcher == null || + !noteExceptionMatcher.matches(classConstant.getName(clazz))) + { + notePrinter.print(clazz.getName(), + classConstant.getName(clazz), + "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 null if it can't be found. + */ + private Clazz findClass(String referencingClassName, String name) + { + // Is it an array type? + if (ClassUtil.isInternalArrayType(name)) + { + // Ignore any primitive array types. + if (!ClassUtil.isInternalClassType(name)) + { + return null; + } + + // Strip the array part. + name = ClassUtil.internalClassNameFromClassType(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 && + missingNotePrinter != null) + { + // We didn't find the superclass or interface. Print a note. + missingNotePrinter.print(referencingClassName, + name, + "Note: " + + ClassUtil.externalClassName(referencingClassName) + + ": can't find dynamically referenced class " + + ClassUtil.externalClassName(name)); + } + } + else if (dependencyWarningPrinter != null) + { + // The superclass or interface was found in the program class pool. + // Print a warning. + dependencyWarningPrinter.print(referencingClassName, + name, + "Warning: library class " + + ClassUtil.externalClassName(referencingClassName) + + " depends dynamically on program class " + + ClassUtil.externalClassName(name)); + } + + return clazz; + } +} diff --git a/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java new file mode 100644 index 000000000..23c8d4045 --- /dev/null +++ b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java @@ -0,0 +1,943 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 + * Class.get[Declared]{Field,Method} 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. + *

+ * It optionally prints notes if on usage of + * (SomeClass)Class.forName(variable).newInstance(). + *

+ * 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, + MemberVisitor +{ + /* + private static boolean DEBUG = true; + /*/ + private static final boolean DEBUG = false; + //*/ + + public static final int CLASS_INDEX = InstructionSequenceMatcher.X; + public static final int MEMBER_NAME_INDEX = InstructionSequenceMatcher.Y; + public static final int TYPE_CLASS_INDEX = InstructionSequenceMatcher.Z; + + public static final int PARAMETER0_CLASS_INDEX = InstructionSequenceMatcher.A; + public static final int PARAMETER1_CLASS_INDEX = InstructionSequenceMatcher.B; + public static final int PARAMETER2_CLASS_INDEX = InstructionSequenceMatcher.C; + public static final int PARAMETER3_CLASS_INDEX = 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_CONSTRUCTOR_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_CONSTRUCTOR_NAME_CLASS_GET_CONSTRUCTOR), + new Utf8Constant(ClassConstants.INTERNAL_CONSTRUCTOR_TYPE_CLASS_GET_CONSTRUCTOR), + }; + + private final Constant[] GET_DECLARED_CONSTRUCTOR_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_CONSTRUCTOR_NAME_CLASS_GET_DECLARED_CONSTRUCTOR), + new Utf8Constant(ClassConstants.INTERNAL_CONSTRUCTOR_TYPE_CLASS_GET_DECLARED_CONSTRUCTOR), + }; + + 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), + }; + + private final Constant[] NEW_INTEGER_UPDATER_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_NEW_UPDATER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_NEW_INTEGER_UPDATER), + }; + + private final Constant[] NEW_LONG_UPDATER_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_NEW_UPDATER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_NEW_LONG_UPDATER), + }; + + private final Constant[] NEW_REFERENCE_UPDATER_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_NEW_UPDATER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_NEW_REFERENCE_UPDATER), + }; + + // SomeClass.class.get[Declared]Field("someField"). + private final Instruction[] CONSTANT_GET_FIELD_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, CLASS_INDEX), + new ConstantInstruction(InstructionConstants.OP_LDC, MEMBER_NAME_INDEX), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + +// // SomeClass.class.get[Declared]Constructor(new Class[] {}). +// private final Instruction[] CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS0 = new Instruction[] +// { +// new ConstantInstruction(InstructionConstants.OP_LDC, CLASS_INDEX), +// new SimpleInstruction(InstructionConstants.OP_ICONST_0), +// new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), +// new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), +// }; +// +// // SomeClass.class.get[Declared]Constructor(new Class[] { A.class }). +// private final Instruction[] CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS1 = new Instruction[] +// { +// new ConstantInstruction(InstructionConstants.OP_LDC, CLASS_INDEX), +// 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, PARAMETER0_CLASS_INDEX), +// new SimpleInstruction(InstructionConstants.OP_AASTORE), +// new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), +// }; +// +// // SomeClass.class.get[Declared]Constructor(new Class[] { A.class, B.class }). +// private final Instruction[] CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS2 = new Instruction[] +// { +// new ConstantInstruction(InstructionConstants.OP_LDC, CLASS_INDEX), +// 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, PARAMETER0_CLASS_INDEX), +// new SimpleInstruction(InstructionConstants.OP_AASTORE), +// new SimpleInstruction(InstructionConstants.OP_DUP), +// new SimpleInstruction(InstructionConstants.OP_ICONST_1), +// new ConstantInstruction(InstructionConstants.OP_LDC, PARAMETER1_CLASS_INDEX), +// new SimpleInstruction(InstructionConstants.OP_AASTORE), +// 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, CLASS_INDEX), + new ConstantInstruction(InstructionConstants.OP_LDC, MEMBER_NAME_INDEX), + 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, CLASS_INDEX), + new ConstantInstruction(InstructionConstants.OP_LDC, MEMBER_NAME_INDEX), + 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, PARAMETER0_CLASS_INDEX), + 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, CLASS_INDEX), + new ConstantInstruction(InstructionConstants.OP_LDC, MEMBER_NAME_INDEX), + 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, PARAMETER0_CLASS_INDEX), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_LDC, PARAMETER1_CLASS_INDEX), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // AtomicIntegerFieldUpdater.newUpdater(A.class, "someField"). + // AtomicLongFieldUpdater.newUpdater(A.class, "someField"). + private final Instruction[] CONSTANT_NEW_PRIMITIVE_UPDATER_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, CLASS_INDEX), + new ConstantInstruction(InstructionConstants.OP_LDC, MEMBER_NAME_INDEX), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + }; + + // AtomicReferenceFieldUpdater.newUpdater(A.class, B.class, "someField"). + private final Instruction[] CONSTANT_NEW_REFERENCE_UPDATER_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, CLASS_INDEX), + new ConstantInstruction(InstructionConstants.OP_LDC, TYPE_CLASS_INDEX), + new ConstantInstruction(InstructionConstants.OP_LDC, MEMBER_NAME_INDEX), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + }; + + // get[Declared]Field("someField"). + private final Instruction[] GET_FIELD_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, MEMBER_NAME_INDEX), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + +// // get[Declared]Constructor(new Class[] {}). +// private final Instruction[] GET_CONSTRUCTOR_INSTRUCTIONS0 = new Instruction[] +// { +// new SimpleInstruction(InstructionConstants.OP_ICONST_0), +// new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), +// new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), +// }; + + // get[Declared]Constructor(new Class[] { A.class }). + private final Instruction[] GET_CONSTRUCTOR_INSTRUCTIONS1 = new Instruction[] + { + 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, PARAMETER0_CLASS_INDEX), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Constructor(new Class[] { A.class, B.class }). + private final Instruction[] GET_CONSTRUCTOR_INSTRUCTIONS2 = new Instruction[] + { + 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, PARAMETER0_CLASS_INDEX), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_LDC, PARAMETER1_CLASS_INDEX), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + 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, MEMBER_NAME_INDEX), + 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, MEMBER_NAME_INDEX), + 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, PARAMETER0_CLASS_INDEX), + 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, MEMBER_NAME_INDEX), + 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, PARAMETER0_CLASS_INDEX), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_LDC, PARAMETER1_CLASS_INDEX), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // AtomicIntegerFieldUpdater.newUpdater(..., "someField"). + // AtomicLongFieldUpdater.newUpdater(..., "someField"). + // AtomicReferenceFieldUpdater.newUpdater(..., "someField"). + private final Instruction[] NEW_UPDATER_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, MEMBER_NAME_INDEX), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 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 constantGetConstructorMatcher0 = +// new InstructionSequenceMatcher(GET_CONSTRUCTOR_CONSTANTS, +// CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS0); +// +// private final InstructionSequenceMatcher constantGetDeclaredConstructorMatcher0 = +// new InstructionSequenceMatcher(GET_DECLARED_CONSTRUCTOR_CONSTANTS, +// CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS0); +// +// private final InstructionSequenceMatcher constantGetConstructorMatcher1 = +// new InstructionSequenceMatcher(GET_CONSTRUCTOR_CONSTANTS, +// CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS1); +// +// private final InstructionSequenceMatcher constantGetDeclaredConstructorMatcher1 = +// new InstructionSequenceMatcher(GET_DECLARED_CONSTRUCTOR_CONSTANTS, +// CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS1); +// +// private final InstructionSequenceMatcher constantGetConstructorMatcher2 = +// new InstructionSequenceMatcher(GET_CONSTRUCTOR_CONSTANTS, +// CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS2); +// +// private final InstructionSequenceMatcher constantGetDeclaredConstructorMatcher2 = +// new InstructionSequenceMatcher(GET_DECLARED_CONSTRUCTOR_CONSTANTS, +// CONSTANT_GET_CONSTRUCTOR_INSTRUCTIONS2); + + 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 constantGetIntegerUpdaterMatcher = + new InstructionSequenceMatcher(NEW_INTEGER_UPDATER_CONSTANTS, + CONSTANT_NEW_PRIMITIVE_UPDATER_INSTRUCTIONS); + + private final InstructionSequenceMatcher constantGetLongUpdaterMatcher = + new InstructionSequenceMatcher(NEW_LONG_UPDATER_CONSTANTS, + CONSTANT_NEW_PRIMITIVE_UPDATER_INSTRUCTIONS); + + private final InstructionSequenceMatcher constantGetReferenceUpdaterMatcher = + new InstructionSequenceMatcher(NEW_REFERENCE_UPDATER_CONSTANTS, + CONSTANT_NEW_REFERENCE_UPDATER_INSTRUCTIONS); + + 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 getConstructorMatcher0 = +// new InstructionSequenceMatcher(GET_CONSTRUCTOR_CONSTANTS, +// GET_CONSTRUCTOR_INSTRUCTIONS0); +// +// private final InstructionSequenceMatcher getDeclaredConstructorMatcher0 = +// new InstructionSequenceMatcher(GET_DECLARED_CONSTRUCTOR_CONSTANTS, +// GET_CONSTRUCTOR_INSTRUCTIONS0); + + private final InstructionSequenceMatcher getConstructorMatcher1 = + new InstructionSequenceMatcher(GET_CONSTRUCTOR_CONSTANTS, + GET_CONSTRUCTOR_INSTRUCTIONS1); + + private final InstructionSequenceMatcher getDeclaredConstructorMatcher1 = + new InstructionSequenceMatcher(GET_DECLARED_CONSTRUCTOR_CONSTANTS, + GET_CONSTRUCTOR_INSTRUCTIONS1); + + private final InstructionSequenceMatcher getConstructorMatcher2 = + new InstructionSequenceMatcher(GET_CONSTRUCTOR_CONSTANTS, + GET_CONSTRUCTOR_INSTRUCTIONS2); + + private final InstructionSequenceMatcher getDeclaredConstructorMatcher2 = + new InstructionSequenceMatcher(GET_DECLARED_CONSTRUCTOR_CONSTANTS, + GET_CONSTRUCTOR_INSTRUCTIONS2); + + 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 InstructionSequenceMatcher getIntegerUpdaterMatcher = + new InstructionSequenceMatcher(NEW_INTEGER_UPDATER_CONSTANTS, + NEW_UPDATER_INSTRUCTIONS); + + private final InstructionSequenceMatcher getLongUpdaterMatcher = + new InstructionSequenceMatcher(NEW_LONG_UPDATER_CONSTANTS, + NEW_UPDATER_INSTRUCTIONS); + + private final InstructionSequenceMatcher getReferenceUpdaterMatcher = + new InstructionSequenceMatcher(NEW_REFERENCE_UPDATER_CONSTANTS, + NEW_UPDATER_INSTRUCTIONS); + + private final MemberFinder memberFinder = new MemberFinder(); + + + // Fields acting as parameters for the visitors. + private Clazz referencedClass; + private String descriptor; + 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, null, null); + + // Try to match the SomeClass.class.getDeclaredField("someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredFieldMatcher, + getDeclaredFieldMatcher, true, true, null, null); + +// // Try to match the SomeClass.class.getConstructor(new Class[] +// // {}) construct. +// matchGetMember(clazz, method, codeAttribute, offset, instruction, +// cnull, //onstantGetConstructorMatcher0, +// getConstructorMatcher0, false, false, +// ClassConstants.INTERNAL_METHOD_NAME_INIT, null); +// +// // Try to match the SomeClass.class.getDeclaredConstructor(new Class[] +// // {}) construct. +// matchGetMember(clazz, method, codeAttribute, offset, instruction, +// null, //constantGetDeclaredConstructorMatcher0, +// getDeclaredConstructorMatcher0, false, true, +// ClassConstants.INTERNAL_METHOD_NAME_INIT, null); + + // Try to match the SomeClass.class.getConstructor(new Class[] + // { A.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + null, //constantGetConstructorMatcher1, + getConstructorMatcher1, false, false, + ClassConstants.INTERNAL_METHOD_NAME_INIT, null); + + // Try to match the SomeClass.class.getDeclaredConstructor(new Class[] + // { A.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + null, //constantGetDeclaredConstructorMatcher1, + getDeclaredConstructorMatcher1, false, true, + ClassConstants.INTERNAL_METHOD_NAME_INIT, null); + + // Try to match the SomeClass.class.getConstructor(new Class[] + // { A.class, B.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + null, //constantGetConstructorMatcher2, + getConstructorMatcher2, false, false, + ClassConstants.INTERNAL_METHOD_NAME_INIT, null); + + // Try to match the SomeClass.class.getDeclaredConstructor(new Class[] + // { A.class, B.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + null, //constantGetDeclaredConstructorMatcher2, + getDeclaredConstructorMatcher2, false, true, + ClassConstants.INTERNAL_METHOD_NAME_INIT, null); + + // Try to match the SomeClass.class.getMethod("someMethod", new Class[] + // {}) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetMethodMatcher0, + getMethodMatcher0, false, false, null, null); + + // Try to match the SomeClass.class.getDeclaredMethod("someMethod", + // new Class[] {}) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredMethodMatcher0, + getDeclaredMethodMatcher0, false, true, null, null); + + // Try to match the SomeClass.class.getMethod("someMethod", new Class[] + // { A.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetMethodMatcher1, + getMethodMatcher1, false, false, null, null); + + // Try to match the SomeClass.class.getDeclaredMethod("someMethod", + // new Class[] { A.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredMethodMatcher1, + getDeclaredMethodMatcher1, false, true, null, null); + + // 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, null, null); + + // 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, null, null); + + // Try to match the AtomicIntegerFieldUpdater.newUpdater( + // SomeClass.class, "someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetIntegerUpdaterMatcher, + getIntegerUpdaterMatcher, true, false, null, + "" + ClassConstants.INTERNAL_TYPE_INT); + + // Try to match the AtomicLongFieldUpdater.newUpdater( + // SomeClass.class, "someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetLongUpdaterMatcher, + getLongUpdaterMatcher, true, false, null, + "" + ClassConstants.INTERNAL_TYPE_LONG); + + // Try to match the AtomicReferenceFieldUpdater.newUpdater( + // SomeClass.class, SomeClass.class, "someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetReferenceUpdaterMatcher, + getReferenceUpdaterMatcher, true, false, null, null); + } + + + /** + * 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, + String defaultName, + String defaultDescriptor) + { + if (constantSequenceMatcher != null) + { + // 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()) + { + initializeStringReference(clazz, + constantSequenceMatcher, + isField, + isDeclared, + defaultDescriptor); + + // 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, + defaultName, + defaultDescriptor); + } + } + + + /** + * Initializes the reference of the matched string constant to the + * referenced class member and its class. + */ + private void initializeStringReference(Clazz clazz, + InstructionSequenceMatcher constantSequenceMatcher, + boolean isField, + boolean isDeclared, + String defaultDescriptor) + { + this.isField = isField; + this.isDeclared = isDeclared; + + // Get the member's class. + int classIndex = constantSequenceMatcher.matchedConstantIndex(CLASS_INDEX); + clazz.constantPoolEntryAccept(classIndex, this); + + // Get the field's reference type, if applicable. + int typeClassIndex = constantSequenceMatcher.matchedConstantIndex(TYPE_CLASS_INDEX); + descriptor = typeClassIndex <= 0 ? defaultDescriptor : + ClassUtil.internalTypeFromClassName(clazz.getClassName(typeClassIndex)); + + // Fill out the matched string constant. + int memberNameIndex = constantSequenceMatcher.matchedConstantIndex(MEMBER_NAME_INDEX); + clazz.constantPoolEntryAccept(memberNameIndex, this); + } + + + // Implementations for ConstantVisitor. + + /** + * Remembers the referenced class. + */ + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + if (DEBUG) + { + System.out.println("DynamicMemberReferenceInitializer: ["+clazz.getName()+"] matched class ["+classConstant.getName(clazz)+"]"); + } + + // 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); + + if (DEBUG) + { + System.out.println("DynamicMemberReferenceInitializer: ["+clazz.getName()+"] matched string ["+name+"]"); + } + + // See if we can find the referenced class member locally, or + // somewhere in the hierarchy. + Member referencedMember = isDeclared ? isField ? + (Member)referencedClass.findField(name, descriptor) : + (Member)referencedClass.findMethod(name, descriptor) : + (Member)memberFinder.findMember(clazz, + referencedClass, + name, + descriptor, + 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, + String defaultName, + String defaultDescriptor) + { + // Print out a note about the dynamic invocation. + if (notePrinter != null && + notePrinter.accepts(clazz.getName())) + { + // Is the class member name in the list of exceptions? + StringMatcher noteExceptionMatcher = isField ? + noteFieldExceptionMatcher : + noteMethodExceptionMatcher; + + int memberNameIndex = noteSequenceMatcher.matchedConstantIndex(MEMBER_NAME_INDEX); + String memberName = memberNameIndex <= 0 ? defaultName : + 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( + PARAMETER0_CLASS_INDEX + 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(clazz.getName(), + "Note: " + + ClassUtil.externalClassName(clazz.getName()) + + " accesses a " + + (isDeclared ? "declared " : "") + + (isField ? "field" : + memberName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? + "constructor" : "method") + + " '" + + externalMemberDescription + + "' dynamically"); + + // Print out notes about potential candidates. + ClassVisitor classVisitor; + + if (isField) + { + classVisitor = defaultDescriptor == null ? + new AllFieldVisitor( + new MemberNameFilter(memberName, this)) : + new AllFieldVisitor( + new MemberNameFilter(memberName, + new MemberDescriptorFilter(defaultDescriptor, this))); + } + else + { + // Compose the partial method descriptor. + String methodDescriptor = "("; + for (int count = 0; count < 2; count++) + { + int memberArgumentIndex = noteSequenceMatcher.matchedConstantIndex(PARAMETER0_CLASS_INDEX + count); + if (memberArgumentIndex > 0) + { + 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) + { + if (notePrinter.accepts(programClass.getName())) + { + System.out.println(" Maybe this is program field '" + + ClassUtil.externalFullClassDescription(0, programClass.getName()) + + " { " + + ClassUtil.externalFullFieldDescription(0, programField.getName(programClass), programField.getDescriptor(programClass)) + + "; }'"); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (notePrinter.accepts(programClass.getName())) + { + System.out.println(" Maybe this is program method '" + + ClassUtil.externalFullClassDescription(0, programClass.getName()) + + " { " + + ClassUtil.externalFullMethodDescription(programClass.getName(), 0, programMethod.getName(programClass), programMethod.getDescriptor(programClass)) + + "; }'"); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (notePrinter.accepts(libraryClass.getName())) + { + System.out.println(" Maybe this is library field '" + + ClassUtil.externalFullClassDescription(0, libraryClass.getName()) + + " { " + + ClassUtil.externalFullFieldDescription(0, libraryField.getName(libraryClass), libraryField.getDescriptor(libraryClass)) + + "; }'"); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (notePrinter.accepts(libraryClass.getName())) + { + System.out.println(" Maybe this is library method '" + + ClassUtil.externalFullClassDescription(0, libraryClass.getName()) + + " { " + + ClassUtil.externalFullMethodDescription(libraryClass.getName(), 0, libraryMethod.getName(libraryClass), libraryMethod.getDescriptor(libraryClass)) + + "; }'"); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/util/EnumFieldReferenceInitializer.java b/src/proguard/classfile/util/EnumFieldReferenceInitializer.java new file mode 100644 index 000000000..dae6db261 --- /dev/null +++ b/src/proguard/classfile/util/EnumFieldReferenceInitializer.java @@ -0,0 +1,150 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.annotation.*; +import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.*; +import proguard.classfile.visitor.*; +import proguard.util.StringMatcher; + +/** + * This ElementValueVisitor initializes the field references of the + * EnumConstantElementValue instances that it visits. + * + * @author Eric Lafortune + */ +public class EnumFieldReferenceInitializer +extends SimplifiedVisitor +implements ElementValueVisitor, + InstructionVisitor, + ConstantVisitor +{ + /* + private static boolean DEBUG = true; + /*/ + private static final boolean DEBUG = false; + //*/ + + private MemberVisitor enumFieldFinder = new AllAttributeVisitor( + new AllInstructionVisitor(this)); + + // Fields acting as parameters and return values for the visitors. + private String enumTypeName; + private String enumConstantName; + private boolean enumConstantNameFound; + private Clazz referencedEnumClass; + private Field referencedEnumField; + + + // Implementations for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {} + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + + if (enumConstantElementValue.referencedClasses != null && + enumConstantElementValue.referencedClasses.length > 0) + { + referencedEnumClass = enumConstantElementValue.referencedClasses[0]; + if (referencedEnumClass != null) + { + // Try to find the enum field through the static enum + // initialization code (at least for program classes). + enumTypeName = enumConstantElementValue.getTypeName(clazz); + enumConstantName = enumConstantElementValue.getConstantName(clazz); + referencedEnumField = null; + referencedEnumClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, + ClassConstants.INTERNAL_METHOD_TYPE_CLINIT, + enumFieldFinder); + + // Otherwise try to find the enum field through its name. + // The constant name could be different from the field name, if + // the latter is already obfuscated. + if (referencedEnumField == null) + { + referencedEnumField = + referencedEnumClass.findField(enumConstantName, + enumTypeName); + } + + if (DEBUG) + { + System.out.println("EnumFieldReferenceInitializer: ["+referencedEnumClass.getName()+"."+enumConstantName+"] -> "+referencedEnumField); + } + + enumConstantElementValue.referencedField = referencedEnumField; + } + } + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_LDC: + case InstructionConstants.OP_LDC_W: + case InstructionConstants.OP_PUTSTATIC: + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + enumConstantNameFound = + enumConstantName.equals(stringConstant.getString(clazz)); + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + if (enumConstantNameFound) + { + if (enumTypeName.equals(fieldrefConstant.getType(clazz))) + { + referencedEnumField = (Field)fieldrefConstant.referencedMember; + } + + enumConstantNameFound = false; + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/util/ExternalTypeEnumeration.java b/src/proguard/classfile/util/ExternalTypeEnumeration.java new file mode 100644 index 000000000..e5b706741 --- /dev/null +++ b/src/proguard/classfile/util/ExternalTypeEnumeration.java @@ -0,0 +1,106 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + + +/** + * An ExternalTypeEnumeration provides an enumeration of all + * types listed in a given external descriptor string. The method name can + * be retrieved separately. + *

+ * A ExternalTypeEnumeration object can be reused for processing + * different subsequent descriptors, by means of the setDescriptor + * method. + * + * @author Eric Lafortune + */ +public class ExternalTypeEnumeration +{ + private String descriptor; + private int index; + + + public ExternalTypeEnumeration(String descriptor) + { + setDescriptor(descriptor); + } + + + ExternalTypeEnumeration() + { + } + + + void setDescriptor(String descriptor) + { + this.descriptor = descriptor; + + reset(); + } + + + public void reset() + { + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) + 1; + + if (index < 1) + { + throw new IllegalArgumentException("Missing opening parenthesis in descriptor ["+descriptor+"]"); + } + } + + + public boolean hasMoreTypes() + { + return index < descriptor.length() - 1; + } + + + public String nextType() + { + int startIndex = index; + + // Find the next separating comma. + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR, + startIndex); + + // Otherwise find the closing parenthesis. + if (index < 0) + { + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE, + startIndex); + if (index < 0) + { + throw new IllegalArgumentException("Missing closing parenthesis in descriptor ["+descriptor+"]"); + } + } + + return descriptor.substring(startIndex, index++).trim(); + } + + + public String methodName() + { + return descriptor.substring(0, descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN)).trim(); + } +} diff --git a/src/proguard/classfile/util/InstructionSequenceMatcher.java b/src/proguard/classfile/util/InstructionSequenceMatcher.java new file mode 100644 index 000000000..8c529a90f --- /dev/null +++ b/src/proguard/classfile/util/InstructionSequenceMatcher.java @@ -0,0 +1,754 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import java.util.Arrays; + +/** + * 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 +{ + /* + public static boolean DEBUG = true; + public static boolean DEBUG_MORE = true; + /*/ + 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 final long[] matchedConstantFlags; + private final int[] matchedConstantIndices; + private int constantFlags; + private int previousConstantFlags; + + // 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]; + matchedConstantFlags = new long[(patternConstants.length + 63) / 64]; + matchedConstantIndices = new int[patternConstants.length]; + } + + + /** + * Starts matching from the first instruction again next time. + */ + public void reset() + { + patternInstructionIndex = 0; + matchedArgumentFlags = 0; + + Arrays.fill(matchedConstantFlags, 0L); + + previousConstantFlags = constantFlags; + constantFlags = 0; + } + + + /** + * Returns whether the complete pattern sequence has been matched. + */ + public boolean isMatching() + { + return matching; + } + + + /** + * Returns the number of instructions in the pattern sequence. + */ + public int instructionCount() + { + return patternInstructions.length; + } + + + /** + * Returns the matched instruction offset of the specified pattern + * instruction. + */ + public int matchedInstructionOffset(int index) + { + return matchedInstructionOffsets[index]; + } + + + /** + * Returns whether the specified wildcard argument was a constant from + * the constant pool in the most recent match. + */ + public boolean wasConstant(int argument) + { + return (previousConstantFlags & (1 << (argument - X))) != 0; + } + + + /** + * Returns the value of the specified matched argument (wildcard or not). + */ + public int matchedArgument(int argument) + { + int argumentIndex = argument - X; + return argumentIndex < 0 ? + argument : + matchedArguments[argumentIndex]; + } + + + /** + * Returns the values of the specified matched arguments (wildcard or not). + */ + 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; + } + + + /** + * Returns the index of the specified matched constant (wildcard or not). + */ + public int matchedConstantIndex(int constantIndex) + { + int argumentIndex = constantIndex - X; + return argumentIndex < 0 ? + matchedConstantIndices[constantIndex] : + matchedArguments[argumentIndex]; + } + + + /** + * Returns the value of the specified matched branch offset (wildcard or + * not). + */ + public int matchedBranchOffset(int offset, int branchOffset) + { + int argumentIndex = branchOffset - X; + return argumentIndex < 0 ? + branchOffset : + matchedArguments[argumentIndex] - offset; + } + + + /** + * Returns the values of the specified matched jump offsets (wildcard or + * not). + */ + 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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + InvokeDynamicConstant invokeDynamicPatternConstant = (InvokeDynamicConstant)patternConstant; + + // Check the bootstrap method and the name and type. + matchingConstant = + matchingConstantIndices(clazz, + invokeDynamicConstant.getBootstrapMethodAttributeIndex(), + invokeDynamicPatternConstant.getBootstrapMethodAttributeIndex()) && + matchingConstantIndices(clazz, + invokeDynamicConstant.getNameAndTypeIndex(), + invokeDynamicPatternConstant.getNameAndTypeIndex()); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + MethodHandleConstant methodHandlePatternConstant = (MethodHandleConstant)patternConstant; + + // Check the handle type and the name and type. + matchingConstant = + matchingArguments(methodHandleConstant.getReferenceKind(), + methodHandlePatternConstant.getReferenceKind()) && + matchingConstantIndices(clazz, + methodHandleConstant.getReferenceIndex(), + methodHandlePatternConstant.getReferenceIndex()); + } + + + 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 visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + MethodTypeConstant typePatternConstant = (MethodTypeConstant)patternConstant; + + // Check the descriptor. + matchingConstant = + matchingConstantIndices(clazz, + methodTypeConstant.u2descriptorIndex, + typePatternConstant.u2descriptorIndex); + } + + + 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 (!isMatchingArgumentIndex(argumentIndex)) + { + // Store the wildcard argument. + setMatchingArgument(argumentIndex, argument1); + + return true; + } + else + { + // Check the previously stored wildcard argument. + return matchedArguments[argumentIndex] == argument1; + } + } + + + /** + * Marks the specified argument (by index) as matching the specified + * argument value. + */ + private void setMatchingArgument(int argumentIndex, + int argument) + { + matchedArguments[argumentIndex] = argument; + matchedArgumentFlags |= 1 << argumentIndex; + } + + + /** + * Returns whether the specified wildcard argument (by index) has been + * matched. + */ + private boolean isMatchingArgumentIndex(int argumentIndex) + { + return (matchedArgumentFlags & (1 << argumentIndex)) != 0; + } + + + 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) + { + // Remember that we are trying to match a constant. + constantFlags |= 1 << (constantIndex2 - X); + + // Check the constant index. + return matchingArguments(constantIndex1, constantIndex2); + } + else if (!isMatchingConstantIndex(constantIndex2)) + { + // 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. + setMatchingConstant(constantIndex2, constantIndex1); + } + } + + return matchingConstant; + } + else + { + // Check a previously stored constant index. + return matchedConstantIndices[constantIndex2] == constantIndex1; + } + } + + + /** + * Marks the specified constant (by index) as matching the specified + * constant index value. + */ + private void setMatchingConstant(int constantIndex, + int constantIndex1) + { + matchedConstantIndices[constantIndex] = constantIndex1; + matchedConstantFlags[constantIndex / 64] |= 1L << constantIndex; + } + + + /** + * Returns whether the specified wildcard constant has been matched. + */ + private boolean isMatchingConstantIndex(int constantIndex) + { + return (matchedConstantFlags[constantIndex / 64] & (1L << constantIndex)) != 0; + } + + + 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 (!isMatchingArgumentIndex(argumentIndex)) + { + // Store a wildcard argument. + setMatchingArgument(argumentIndex, offset + branchOffset1); + + 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 new file mode 100644 index 000000000..9c63ad9e3 --- /dev/null +++ b/src/proguard/classfile/util/InternalTypeEnumeration.java @@ -0,0 +1,204 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + + +/** + * An InternalTypeEnumeration provides an enumeration of all + * parameter types listed in a given internal method descriptor or signature. + * The signature can also be a class signature. The return type of a method + * descriptor can be retrieved separately. + * + * @author Eric Lafortune + */ +public class InternalTypeEnumeration +{ + private String descriptor; + private int firstIndex; + private int lastIndex; + private int index; + + + /** + * Creates a new InternalTypeEnumeration for the given method descriptor. + */ + public InternalTypeEnumeration(String descriptor) + { + this.descriptor = descriptor; + this.firstIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + this.lastIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + this.index = firstIndex + 1; + + if (lastIndex < 0) + { + lastIndex = descriptor.length(); + } + } + + + /** + * Returns the formal type parameters from the descriptor, assuming it's a + * method descriptor. + */ + public String formalTypeParameters() + { + return descriptor.substring(0, firstIndex); + } + + + /** + * Returns whether the enumeration can provide more types from the method + * descriptor. + */ + public boolean hasMoreTypes() + { + return index < lastIndex; + } + + + /** + * Returns the next type from the method descriptor. + */ + public String nextType() + { + int startIndex = index; + + skipArray(); + + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_CLASS_START: + case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START: + { + skipClass(); + break; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + { + skipGeneric(); + break; + } + } + + return descriptor.substring(startIndex, index); + } + + + /** + * Returns the return type from the descriptor, assuming it's a method + * descriptor. + */ + public String returnType() + { + return descriptor.substring(lastIndex + 1); + } + + + // Small utility methods. + + private void skipArray() + { + while (descriptor.charAt(index) == ClassConstants.INTERNAL_TYPE_ARRAY) + { + index++; + } + } + + + private void skipClass() + { + while (true) + { + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + skipGeneric(); + break; + + case ClassConstants.INTERNAL_TYPE_CLASS_END: + return; + } + } + } + + + private void skipGeneric() + { + int nestingLevel = 1; + + do + { + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + nestingLevel++; + break; + + case ClassConstants.INTERNAL_TYPE_GENERIC_END: + nestingLevel--; + break; + } + } + while (nestingLevel > 0); + } + + + /** + * A main method for testing the type enumeration. + */ + public static void main(String[] args) + { + try + { + for (int index = 0; index < args.length; index++) + { + String descriptor = args[index]; + + System.out.println("Descriptor ["+descriptor+"]"); + InternalTypeEnumeration enumeration = new InternalTypeEnumeration(descriptor); + + if (enumeration.firstIndex >= 0) + { + System.out.println(" Formal type parameters ["+enumeration.formalTypeParameters()+"]"); + } + + while (enumeration.hasMoreTypes()) + { + System.out.println(" Type ["+enumeration.nextType()+"]"); + } + + if (enumeration.lastIndex < descriptor.length()) + { + System.out.println(" Return type ["+enumeration.returnType()+"]"); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/classfile/util/MemberFinder.java b/src/proguard/classfile/util/MemberFinder.java new file mode 100644 index 000000000..36c000362 --- /dev/null +++ b/src/proguard/classfile/util/MemberFinder.java @@ -0,0 +1,197 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 provides methods to find class members in a given class or in its + * hierarchy. + * + * @author Eric Lafortune + */ +public class MemberFinder +extends SimplifiedVisitor +implements MemberVisitor +{ + private static class MemberFoundException extends RuntimeException {} + private static final MemberFoundException MEMBER_FOUND = new MemberFoundException(); + + private Clazz clazz; + private Member member; + + + /** + * Finds the field with the given name and descriptor in the given + * class or its hierarchy. + */ + public Field findField(Clazz referencingClass, + Clazz clazz, + String name, + String descriptor) + { + return (Field)findMember(referencingClass, clazz, name, descriptor, true); + } + + + /** + * Finds the method with the given name and descriptor in the given + * class or its hierarchy. + */ + public Method findMethod(Clazz referencingClass, + Clazz clazz, + String name, + String descriptor) + { + return (Method)findMember(referencingClass, clazz, name, descriptor, false); + } + + + /** + * Finds the class member with the given name and descriptor in the given + * class or its hierarchy. + */ + 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.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 member; + } + + + /** + * Returns the corresponding class of the most recently found class + * member. + */ + public Clazz correspondingClass() + { + return clazz; + } + + + /** + * Returns whether the given method is overridden anywhere down the class + * hierarchy. + */ + public boolean isOverriden(Clazz clazz, + Method method) + { + String name = method.getName(clazz); + String descriptor = method.getDescriptor(clazz); + + // Go looking for the method down the class hierarchy. + try + { + 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) + { + // We've found an overriding method. + return true; + } + + return false; + } + + + /** + * Returns whether the given field is shadowed anywhere down the class + * hierarchy. + */ + public boolean isShadowed(Clazz clazz, + Field field) + { + String name = field.getName(clazz); + String descriptor = field.getDescriptor(clazz); + + // Go looking for the field down the class hierarchy. + try + { + 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 a shadowing field. + return true; + } + + return false; + } + + +// // Implementations for ClassVisitor. +// +// private void visitAnyClass(Clazz clazz) +// { +// if (member == null) +// { +// member = isField ? +// (Member)clazz.findField(name, descriptor) : +// (Member)clazz.findMethod(name, descriptor); +// +// if (member != null) +// { +// this.clazz = clazz; +// } +// } +// } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + this.clazz = clazz; + this.member = member; + + throw MEMBER_FOUND; + } +} diff --git a/src/proguard/classfile/util/MethodLinker.java b/src/proguard/classfile/util/MethodLinker.java new file mode 100644 index 000000000..56b6723fb --- /dev/null +++ b/src/proguard/classfile/util/MethodLinker.java @@ -0,0 +1,160 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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, non-static, + * non-initializer 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. + * + * @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 | ClassConstants.INTERNAL_ACC_STATIC, + 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: and 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; + } + + // 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) + { + // Get the last method in the chain. + Member thisLastMember = lastMember(member); + + // Store the new class method in the map. + memberMap.put(key, thisLastMember); + } + else + { + // Link both members. + link(member, otherMember); + } + } + + + // Small utility methods. + + /** + * Links the two given class members. + */ + private static void link(Member member1, Member member2) + { + // Get the last methods in the both chains. + Member lastMember1 = lastMember(member1); + Member lastMember2 = lastMember(member2); + + // Check if both link chains aren't already ending in the same element. + if (!lastMember1.equals(lastMember2)) + { + // Merge the two chains, with the library members last. + if (lastMember2 instanceof LibraryMember) + { + lastMember1.setVisitorInfo(lastMember2); + } + else + { + lastMember2.setVisitorInfo(lastMember1); + } + } + } + + + /** + * 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 000000000..aed46ebba --- /dev/null +++ b/src/proguard/classfile/util/SimplifiedVisitor.java @@ -0,0 +1,834 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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); + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + visitAnyConstant(clazz, invokeDynamicConstant); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + visitAnyConstant(clazz, methodHandleConstant); + } + + + /** + * 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 visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + visitAnyConstant(clazz, methodTypeConstant); + } + + + 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 visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + visitAnyAttribute(clazz, bootstrapMethodsAttribute); + } + + + 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/StringReferenceInitializer.java b/src/proguard/classfile/util/StringReferenceInitializer.java new file mode 100644 index 000000000..f00f0d359 --- /dev/null +++ b/src/proguard/classfile/util/StringReferenceInitializer.java @@ -0,0 +1,90 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This ConstantVisitor initializes any class references of all string constants + * it visits. More specifically, it fills out the references of string constant + * pool entries that happen to refer to a class in the program class pool or in + * the library class pool. + * + * @author Eric Lafortune + */ +public class StringReferenceInitializer +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + + + /** + * Creates a new StringReferenceInitializer. + */ + public StringReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + if (stringConstant.referencedClass == null) + { + // See if we can find the referenced class. + stringConstant.referencedClass = + findClass(ClassUtil.internalClassName( + ClassUtil.externalBaseType(stringConstant.getString(clazz)))); + } + } + + + // Small utility methods. + + /** + * Returns the class with the given name, either for the program class pool + * or from the library class pool, or null if it can't be found. + */ + private Clazz findClass(String name) + { + // First look for the class in the program class pool. + Clazz clazz = programClassPool.getClass(name); + + // Otherwise look for the class in the library class pool. + if (clazz == null) + { + clazz = libraryClassPool.getClass(name); + } + + return clazz; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/util/StringSharer.java b/src/proguard/classfile/util/StringSharer.java new file mode 100644 index 000000000..dacb2d13f --- /dev/null +++ b/src/proguard/classfile/util/StringSharer.java @@ -0,0 +1,172 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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(); + } + + // Replace the interface name strings by the shared name strings. + 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. + Clazz interfaceClass = interfaceClasses[index]; + if (interfaceClass != null) + { + interfaceNames[index] = interfaceClass.getName(); + } + } + } + } + + + // Implementations for ConstantVisitor. + + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(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 new file mode 100644 index 000000000..39172ddd6 --- /dev/null +++ b/src/proguard/classfile/util/WarningPrinter.java @@ -0,0 +1,136 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.*; + +import java.io.PrintStream; +import java.util.List; + +/** + * This class prints out and counts warnings. + * + * @author Eric Lafortune + */ +public class WarningPrinter +{ + private final PrintStream printStream; + private final StringMatcher classFilter; + private int warningCount; + + + /** + * Creates a new WarningPrinter that prints to the System.err print stream. + */ + public WarningPrinter() + { + this(System.err); + } + + + /** + * Creates a new WarningPrinter that prints to the given print stream. + */ + public WarningPrinter(PrintStream printStream) + { + this.printStream = printStream; + this.classFilter = null; + } + + + /** + * Creates a new WarningPrinter that prints to the given print stream, + * except if the names of any involved classes matches the given filter. + */ + public WarningPrinter(PrintStream printStream, List classFilter) + { + this.printStream = printStream; + this.classFilter = classFilter == null ? null : + new ListParser(new ClassNameParser()).parse(classFilter); + } + + + /** + * Prints out the given warning and increments the warning count, if + * the given class name passes the class name filter. + */ + public void print(String className, String warning) + { + if (accepts(className)) + { + print(warning); + } + } + + + /** + * Returns whether the given class name passes the class name filter. + */ + public boolean accepts(String className) + { + return classFilter == null || + !classFilter.matches(className); + } + + + /** + * Prints out the given warning and increments the warning count, if + * the given class names pass the class name filter. + */ + public void print(String className1, String className2, String warning) + { + if (accepts(className1, className2)) + { + print(warning); + } + } + + + /** + * Returns whether the given class names pass the class name filter. + */ + public boolean accepts(String className1, String className2) + { + return classFilter == null || + !(classFilter.matches(className1) || + classFilter.matches(className2)); + } + + + /** + * Prints out the given warning and increments the warning count. + */ + private void print(String warning) + { + printStream.println(warning); + + warningCount++; + } + + + /** + * Returns the number of warnings printed so far. + */ + public int getWarningCount() + { + return warningCount; + } +} diff --git a/src/proguard/classfile/util/package.html b/src/proguard/classfile/util/package.html new file mode 100644 index 000000000..b1b881ec5 --- /dev/null +++ b/src/proguard/classfile/util/package.html @@ -0,0 +1,3 @@ + +This package contains utility classes for processing class files. + diff --git a/src/proguard/classfile/visitor/AllClassVisitor.java b/src/proguard/classfile/visitor/AllClassVisitor.java new file mode 100644 index 000000000..5c6f3defd --- /dev/null +++ b/src/proguard/classfile/visitor/AllClassVisitor.java @@ -0,0 +1,47 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; + + +/** + * This ClassPoolVisitor lets a given ClassVisitor visit all Clazz + * objects of the class pools it visits. + * + * @author Eric Lafortune + */ +public class AllClassVisitor implements ClassPoolVisitor +{ + private final ClassVisitor classVisitor; + + + public AllClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + public void visitClassPool(ClassPool classPool) + { + classPool.classesAccept(classVisitor); + } +} diff --git a/src/proguard/classfile/visitor/AllFieldVisitor.java b/src/proguard/classfile/visitor/AllFieldVisitor.java new file mode 100644 index 000000000..92c4b0513 --- /dev/null +++ b/src/proguard/classfile/visitor/AllFieldVisitor.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 lets a given MemberVisitor visit all FieldMember + * objects of the classes it visits. + * + * @author Eric Lafortune + */ +public class AllFieldVisitor implements ClassVisitor +{ + private final MemberVisitor memberVisitor; + + + public AllFieldVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.fieldsAccept(memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.fieldsAccept(memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/AllMemberVisitor.java b/src/proguard/classfile/visitor/AllMemberVisitor.java new file mode 100644 index 000000000..ab26bf34e --- /dev/null +++ b/src/proguard/classfile/visitor/AllMemberVisitor.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 lets a given MemberVisitor visit all Member + * objects of the classes it visits. + * + * @author Eric Lafortune + */ +public class AllMemberVisitor implements ClassVisitor +{ + private final MemberVisitor memberVisitor; + + + public AllMemberVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.fieldsAccept(memberVisitor); + programClass.methodsAccept(memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.fieldsAccept(memberVisitor); + libraryClass.methodsAccept(memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/AllMethodVisitor.java b/src/proguard/classfile/visitor/AllMethodVisitor.java new file mode 100644 index 000000000..5d8e6a39e --- /dev/null +++ b/src/proguard/classfile/visitor/AllMethodVisitor.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 lets a given MemberVisitor visit all MethodMember + * objects of the classes it visits. + * + * @author Eric Lafortune + */ +public class AllMethodVisitor implements ClassVisitor +{ + private final MemberVisitor memberVisitor; + + + public AllMethodVisitor(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.methodsAccept(memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.methodsAccept(memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/BottomClassFilter.java b/src/proguard/classfile/visitor/BottomClassFilter.java new file mode 100644 index 000000000..e094ce3b6 --- /dev/null +++ b/src/proguard/classfile/visitor/BottomClassFilter.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * ClassVisitor, but only when visiting classes that don't + * have any subclasses. + * + * @author Eric Lafortune + */ +public class BottomClassFilter implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ProgramClassFilter. + * @param classVisitor the ClassVisitor to which visits + * will be delegated. + */ + public BottomClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Is this a bottom class in the class hierarchy? + if (programClass.subClasses == null) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Is this a bottom class in the class hierarchy? + if (libraryClass.subClasses == null) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} diff --git a/src/proguard/classfile/visitor/ClassAccessFilter.java b/src/proguard/classfile/visitor/ClassAccessFilter.java new file mode 100644 index 000000000..18556627f --- /dev/null +++ b/src/proguard/classfile/visitor/ClassAccessFilter.java @@ -0,0 +1,88 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * ClassVisitor, but only when the visited class + * has the proper access flags. + * + * @see ClassConstants + * + * @author Eric Lafortune + */ +public class ClassAccessFilter implements ClassVisitor +{ + private final int requiredSetAccessFlags; + private final int requiredUnsetAccessFlags; + private final ClassVisitor classVisitor; + + + /** + * 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 classVisitor the ClassVisitor to + * which visits will be delegated. + */ + public ClassAccessFilter(int requiredSetAccessFlags, + int requiredUnsetAccessFlags, + ClassVisitor classVisitor) + { + this.requiredSetAccessFlags = requiredSetAccessFlags; + this.requiredUnsetAccessFlags = requiredUnsetAccessFlags; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (accepted(programClass.getAccessFlags())) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (accepted(libraryClass.getAccessFlags())) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private boolean accepted(int accessFlags) + { + return (requiredSetAccessFlags & ~accessFlags) == 0 && + (requiredUnsetAccessFlags & accessFlags) == 0; + } +} diff --git a/src/proguard/classfile/visitor/ClassCleaner.java b/src/proguard/classfile/visitor/ClassCleaner.java new file mode 100644 index 000000000..a7ad1f6f7 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ClassVisitor 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/visitor/ClassCollector.java b/src/proguard/classfile/visitor/ClassCollector.java new file mode 100644 index 000000000..a24bb0b2b --- /dev/null +++ b/src/proguard/classfile/visitor/ClassCollector.java @@ -0,0 +1,58 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.SimplifiedVisitor; + +import java.util.Set; + +/** + * This ClassVisitor collects the classes that it visits in the + * given collection. + * + * @author Eric Lafortune + */ +public class ClassCollector +extends SimplifiedVisitor +implements ClassVisitor +{ + private final Set set; + + + /** + * Creates a new ClassCollector. + * @param set the Set 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 new file mode 100644 index 000000000..b6deef2b6 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassCounter.java @@ -0,0 +1,56 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 counts the number of classes that has been visited. + * + * @author Eric Lafortune + */ +public class ClassCounter implements ClassVisitor +{ + private int count; + + + /** + * Returns the number of classes that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) + { + count++; + } + + + public void visitProgramClass(ProgramClass programClass) + { + count++; + } +} diff --git a/src/proguard/classfile/visitor/ClassHierarchyTraveler.java b/src/proguard/classfile/visitor/ClassHierarchyTraveler.java new file mode 100644 index 000000000..38ba3d624 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 lets a given ClassVisitor + * 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 ClassVisitor 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/ClassNameFilter.java b/src/proguard/classfile/visitor/ClassNameFilter.java new file mode 100644 index 000000000..bd66eb1c7 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassNameFilter.java @@ -0,0 +1,112 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +import java.util.List; + +/** + * This ClassVisitor delegates its visits to another given + * ClassVisitor, 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 ClassVisitor to which visits + * will be delegated. + */ + public ClassNameFilter(String regularExpression, + ClassVisitor classVisitor) + { + this(new ListParser(new ClassNameParser()).parse(regularExpression), + classVisitor); + } + + + /** + * Creates a new ClassNameFilter. + * @param regularExpression the regular expression against which class names + * will be matched. + * @param classVisitor the ClassVisitor to which visits + * will be delegated. + */ + public ClassNameFilter(List regularExpression, + ClassVisitor classVisitor) + { + this(new ListParser(new ClassNameParser()).parse(regularExpression), + classVisitor); + } + + + /** + * Creates a new ClassNameFilter. + * @param regularExpressionMatcher the string matcher against which + * class names will be matched. + * @param classVisitor the ClassVisitor to which + * visits will be delegated. + */ + public ClassNameFilter(StringMatcher regularExpressionMatcher, + ClassVisitor classVisitor) + { + this.regularExpressionMatcher = regularExpressionMatcher; + 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 new file mode 100644 index 000000000..ae234bef9 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassPoolFiller.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; + + +/** + * This ClassVisitor collects all the classes it visits in a given + * class pool. + * + * @author Eric Lafortune + */ +public class ClassPoolFiller +extends SimplifiedVisitor +implements ClassVisitor +{ + private final ClassPool classPool; + + + /** + * Creates a new ClassPoolFiller. + */ + public ClassPoolFiller(ClassPool classPool) + { + this.classPool = classPool; + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + classPool.addClass(clazz); + } +} diff --git a/src/proguard/classfile/visitor/ClassPoolVisitor.java b/src/proguard/classfile/visitor/ClassPoolVisitor.java new file mode 100644 index 000000000..821304ac9 --- /dev/null +++ b/src/proguard/classfile/visitor/ClassPoolVisitor.java @@ -0,0 +1,37 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; + + +/** + * This interface specifies the methods for a visitor of + * ClassPool objects. Note that there is only a single + * implementation of ClassPool, such that this interface + * is not strictly necessary as a visitor. + * + * @author Eric Lafortune + */ +public interface ClassPoolVisitor +{ + public void visitClassPool(ClassPool classPool); +} diff --git a/src/proguard/classfile/visitor/ClassPresenceFilter.java b/src/proguard/classfile/visitor/ClassPresenceFilter.java new file mode 100644 index 000000000..0c55d1d16 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to one of two + * ClassVisitor instances, depending on whether the name of + * the visited class file is present in a given ClassPool 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 ClassPool in which the + * presence will be tested. + * @param presentClassVisitor the ClassVisitor to which visits + * of present class files will be delegated. + * @param missingClassVisitor the ClassVisitor 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 ClassVisitor. + */ + 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 000000000..3121e58de --- /dev/null +++ b/src/proguard/classfile/visitor/ClassPrinter.java @@ -0,0 +1,1012 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ClassVisitor prints out the complete internal + * structure of the classes it visits. + * + * @author Eric Lafortune + */ +public class ClassPrinter +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + BootstrapMethodInfoVisitor, + InnerClassesInfoVisitor, + ExceptionInfoVisitor, + 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 System.out. + */ + public ClassPrinter() + { + this(System.out); + } + + + /** + * Creates a new ClassPrinter that prints to the given + * PrintStream. + */ + 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(" = target " + ClassUtil.externalClassVersion(programClass.u4version)); + println("Access flags: 0x" + Integer.toHexString(programClass.u2accessFlags)); + println(" = " + + ((programClass.u2accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0 ? "@ " : "") + + ClassUtil.externalClassAccessFlags(programClass.u2accessFlags) + + ((programClass.u2accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0 ? "enum " : + (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(); + programClass.interfaceConstantsAccept(this); + 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(" = " + + ((libraryClass.u2accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0 ? "@ " : "") + + ClassUtil.externalClassAccessFlags(libraryClass.u2accessFlags) + + ((libraryClass.u2accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0 ? "enum " : + (libraryClass.u2accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) == 0 ? "class " : + "") + + ClassUtil.externalClassName(libraryClass.getName()) + + (libraryClass.getSuperName() == null ? "" : " 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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + println(visitorInfo(invokeDynamicConstant) + " InvokeDynamic [bootstrap method index = " + invokeDynamicConstant.u2bootstrapMethodAttributeIndex + "]:"); + + indent(); + clazz.constantPoolEntryAccept(invokeDynamicConstant.u2nameAndTypeIndex, this); + outdent(); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + println(visitorInfo(methodHandleConstant) + " MethodHandle [kind = " + methodHandleConstant.u1referenceKind + "]:"); + + indent(); + clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, this); + outdent(); + } + + + 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 visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + println(visitorInfo(methodTypeConstant) + " MethodType [" + + clazz.getString(methodTypeConstant.u2descriptorIndex) + "]"); + } + + + 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 visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + println(visitorInfo(bootstrapMethodsAttribute) + + " Bootstrap methods attribute (count = " + bootstrapMethodsAttribute.u2bootstrapMethodsCount + "):"); + + indent(); + bootstrapMethodsAttribute.bootstrapMethodEntriesAccept(clazz, this); + outdent(); + } + + + 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:"); + + indent(); + clazz.constantPoolEntryAccept(enclosingMethodAttribute.u2classIndex, this); + + if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) + { + clazz.constantPoolEntryAccept(enclosingMethodAttribute.u2nameAndTypeIndex, this); + } + outdent(); + } + + + 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 (parameter count = " + runtimeVisibleParameterAnnotationsAttribute.u2parametersCount + "):"); + + indent(); + runtimeVisibleParameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + outdent(); + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + println(visitorInfo(runtimeInvisibleParameterAnnotationsAttribute) + + " Runtime invisible parameter annotations attribute (parameter count = " + runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount + "):"); + + 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 BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + println(visitorInfo(bootstrapMethodInfo) + + " BootstrapMethodInfo (argument count = " + + bootstrapMethodInfo.u2methodArgumentCount+ "):"); + + indent(); + clazz.constantPoolEntryAccept(bootstrapMethodInfo.u2methodHandleIndex, this); + bootstrapMethodInfo.methodArgumentsAccept(clazz, this); + outdent(); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + println(visitorInfo(innerClassesInfo) + + " InnerClassesInfo:"); + + indent(); + println("Access flags: 0x" + Integer.toHexString(innerClassesInfo.u2innerClassAccessFlags) + " = " + + ClassUtil.externalClassAccessFlags(innerClassesInfo.u2innerClassAccessFlags)); + innerClassesInfo.innerClassConstantAccept(clazz, this); + innerClassesInfo.outerClassConstantAccept(clazz, this); + innerClassesInfo.innerNameConstantAccept(clazz, 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("v" + 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("v" + 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 000000000..73bcc6a0c --- /dev/null +++ b/src/proguard/classfile/visitor/ClassVersionFilter.java @@ -0,0 +1,85 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to program classes to + * another given ClassVisitor, 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 classVisitor the ClassVisitor to which visits + * will be delegated. + */ + public ClassVersionFilter(int minimumClassVersion, + ClassVisitor classVisitor) + { + this(minimumClassVersion, Integer.MAX_VALUE, classVisitor); + } + + + /** + * Creates a new ClassVersionFilter. + * @param minimumClassVersion the minimum class version number. + * @param maximumClassVersion the maximum class version number. + * @param classVisitor the ClassVisitor 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 000000000..d3f018319 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.util.Set; + +/** + * This ClassVisitor 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 Set 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/ClassVisitor.java b/src/proguard/classfile/visitor/ClassVisitor.java new file mode 100644 index 000000000..c4234469e --- /dev/null +++ b/src/proguard/classfile/visitor/ClassVisitor.java @@ -0,0 +1,36 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 + * Clazz objects. + * + * @author Eric Lafortune + */ +public interface ClassVisitor +{ + public void visitProgramClass(ProgramClass programClass); + public void visitLibraryClass(LibraryClass libraryClass); +} diff --git a/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java b/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java new file mode 100644 index 000000000..0b971f0b1 --- /dev/null +++ b/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java @@ -0,0 +1,100 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 lets a given ClassVisitor + * travel to the first concrete subclasses down in its hierarchy of abstract + * classes and concrete classes. + * + * @author Eric Lafortune + */ +public class ConcreteClassDownTraveler +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ConcreteClassDownTraveler. + * @param classVisitor the ClassVisitor to + * which visits will be delegated. + */ + public ConcreteClassDownTraveler(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Is this an abstract class or an interface? + if ((programClass.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_INTERFACE | + ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0) + { + // Travel down the hierarchy. + Clazz[] subClasses = programClass.subClasses; + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].accept(this); + } + } + } + else + { + // Visit the class. Don't descend any further. + programClass.accept(classVisitor); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Is this an abstract class or interface? + if ((libraryClass.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_INTERFACE | + ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0) + { + // Travel down the hierarchy. + Clazz[] subClasses = libraryClass.subClasses; + if (subClasses != null) + { + for (int index = 0; index < subClasses.length; index++) + { + subClasses[index].accept(this); + } + } + } + else + { + // Visit the class. Don't descend any further. + libraryClass.accept(classVisitor); + } + } +} diff --git a/src/proguard/classfile/visitor/DotClassClassVisitor.java b/src/proguard/classfile/visitor/DotClassClassVisitor.java new file mode 100644 index 000000000..979f84675 --- /dev/null +++ b/src/proguard/classfile/visitor/DotClassClassVisitor.java @@ -0,0 +1,89 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ClassVisitor visit all + * classes involved in any .class constructs that it visits. + *

+ * Note that before JDK 1.5, .class constructs are actually + * compiled differently, using Class.forName constructs. + * + * @author Eric Lafortune + */ +public class DotClassClassVisitor +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassHierarchyTraveler. + * @param classVisitor the ClassVisitor 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/ExceptClassFilter.java b/src/proguard/classfile/visitor/ExceptClassFilter.java new file mode 100644 index 000000000..25c6e68ef --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptClassFilter.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * ClassVisitor, except for one given class. + * + * @author Eric Lafortune + */ +public class ExceptClassFilter implements ClassVisitor +{ + private final Clazz exceptClass; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassNameFilter. + * @param exceptClass the class that will not be visited. + * @param classVisitor the ClassVisitor to which visits will + * be delegated. + */ + public ExceptClassFilter(Clazz exceptClass, + ClassVisitor classVisitor) + { + this.exceptClass = exceptClass; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!programClass.equals(exceptClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (!libraryClass.equals(exceptClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/ExceptClassesFilter.java b/src/proguard/classfile/visitor/ExceptClassesFilter.java new file mode 100644 index 000000000..bdf72bdec --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptClassesFilter.java @@ -0,0 +1,90 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * ClassVisitor, except for classes are in a given list. + * + * @author Eric Lafortune + */ +public class ExceptClassesFilter implements ClassVisitor +{ + private final Clazz[] exceptClasses; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ExceptClassesFilter. + * @param exceptClasses the classes that will not be visited. + * @param classVisitor the ClassVisitor to which visits will + * be delegated. + */ + public ExceptClassesFilter(Clazz[] exceptClasses, + ClassVisitor classVisitor) + { + this.exceptClasses = exceptClasses; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!present(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (!present(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private boolean present(Clazz clazz) + { + if (exceptClasses == null) + { + return false; + } + + for (int index = 0; index < exceptClasses.length; index++) + { + if (exceptClasses[index].equals(clazz)) + { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/ExceptionCounter.java b/src/proguard/classfile/visitor/ExceptionCounter.java new file mode 100644 index 000000000..5c476b656 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionCounter.java @@ -0,0 +1,52 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor counts the number of exceptions that has been visited. + * + * @author Eric Lafortune + */ +public class ExceptionCounter implements ExceptionInfoVisitor +{ + private int count; + + + /** + * Returns the number of exceptions that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + count++; + } +} diff --git a/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java b/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java new file mode 100644 index 000000000..2fd18aedb --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor delegates its visits to another given + * ExceptionInfoVisitor, 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/ExceptionHandlerConstantVisitor.java b/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java new file mode 100644 index 000000000..de7139bc3 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.visitor.ConstantVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor lets a given + * ConstantVisitor visit all catch class constants of exceptions + * that it visits. + * + * @author Eric Lafortune + */ +public class ExceptionHandlerConstantVisitor +implements ExceptionInfoVisitor +{ + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ExceptionHandlerConstantVisitor. + * @param constantVisitor the ConstantVisitor that will visit the catch + * class constants. + */ + public ExceptionHandlerConstantVisitor(ConstantVisitor constantVisitor) + { + this.constantVisitor = constantVisitor; + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + int catchType = exceptionInfo.u2catchType; + if (catchType != 0) + { + clazz.constantPoolEntryAccept(catchType, constantVisitor); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/ExceptionHandlerFilter.java b/src/proguard/classfile/visitor/ExceptionHandlerFilter.java new file mode 100644 index 000000000..36ead5ec9 --- /dev/null +++ b/src/proguard/classfile/visitor/ExceptionHandlerFilter.java @@ -0,0 +1,70 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor delegates its visits to another given + * ExceptionInfoVisitor, but only when the visited exception + * targets an instruction in the given range of offsets. + * + * @author Eric Lafortune + */ +public class ExceptionHandlerFilter +implements ExceptionInfoVisitor +{ + private final int startOffset; + private final int endOffset; + private final ExceptionInfoVisitor exceptionInfoVisitor; + + + /** + * Creates a new ExceptionHandlerFilter. + * @param startOffset the start of the instruction offset range. + * @param endOffset the end of the instruction offset range. + * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits + * will be delegated. + */ + public ExceptionHandlerFilter(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) + { + int handlerPC = exceptionInfo.u2handlerPC; + if (handlerPC >= startOffset && + handlerPC < endOffset) + { + exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/ExceptionOffsetFilter.java b/src/proguard/classfile/visitor/ExceptionOffsetFilter.java new file mode 100644 index 000000000..c84473ab6 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor delegates its visits to another given + * ExceptionInfoVisitor, 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 000000000..626a32e23 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ExceptionInfoVisitor; + +/** + * This ExceptionInfoVisitor delegates its visits to another given + * ExceptionInfoVisitor, 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/ImplementedClassConstantFilter.java b/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java new file mode 100644 index 000000000..334b85f76 --- /dev/null +++ b/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.visitor.ConstantVisitor; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor delegates its visits to class constants + * to another given ConstantVisitor, except for classes that + * extend or implement a given class. This exception includes the class itself. + * + * @author Eric Lafortune + */ +public class ImplementedClassConstantFilter +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final Clazz implementedClass; + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ImplementedClassConstantFilter. + * @param implementedClass the class whose implementations will not be + * visited. + * @param constantVisitor the ConstantVisitor to which visits + * will be delegated. + */ + public ImplementedClassConstantFilter(Clazz implementedClass, + ConstantVisitor constantVisitor) + { + this.implementedClass = implementedClass; + this.constantVisitor = constantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass == null || + !referencedClass.extendsOrImplements(implementedClass)) + { + constantVisitor.visitClassConstant(clazz, classConstant); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/ImplementedClassFilter.java b/src/proguard/classfile/visitor/ImplementedClassFilter.java new file mode 100644 index 000000000..abbacfb9d --- /dev/null +++ b/src/proguard/classfile/visitor/ImplementedClassFilter.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * ClassVisitor, except for classes that extend or implement + * a given class. + * + * @author Eric Lafortune + */ +public class ImplementedClassFilter implements ClassVisitor +{ + private final Clazz implementedClass; + private final ClassVisitor classVisitor; + + + /** + * Creates a new ImplementedClassFilter. + * @param implementedClass the class whose implementations will not be + * visited. + * @param classVisitor the ClassVisitor to which visits will + * be delegated. + */ + public ImplementedClassFilter(Clazz implementedClass, + ClassVisitor classVisitor) + { + this.implementedClass = implementedClass; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!programClass.extendsOrImplements(implementedClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (!libraryClass.extendsOrImplements(implementedClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java b/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java new file mode 100644 index 000000000..8e7010de5 --- /dev/null +++ b/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java @@ -0,0 +1,70 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.visitor.ConstantVisitor; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This ConstantVisitor delegates its visits to class constants + * to another given ConstantVisitor, except for classes that + * are extended or implemented by a given class. This exception includes the + * class itself. + * + * @author Eric Lafortune + */ +public class ImplementingClassConstantFilter +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final Clazz implementingClass; + private final ConstantVisitor constantVisitor; + + + /** + * Creates a new ImplementingClassConstantFilter. + * @param implementingClass the class whose superclasses and interfaces will + * not be visited. + * @param constantVisitor the ConstantVisitor to which visits + * will be delegated. + */ + public ImplementingClassConstantFilter(Clazz implementingClass, + ConstantVisitor constantVisitor) + { + this.implementingClass = implementingClass; + this.constantVisitor = constantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass == null || + !implementingClass.extendsOrImplements(referencedClass)) + { + constantVisitor.visitClassConstant(clazz, classConstant); + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/LibraryClassFilter.java b/src/proguard/classfile/visitor/LibraryClassFilter.java new file mode 100644 index 000000000..7437ed3c8 --- /dev/null +++ b/src/proguard/classfile/visitor/LibraryClassFilter.java @@ -0,0 +1,60 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * ClassVisitor, but only when visiting library classes. + * + * @author Eric Lafortune + */ +public class LibraryClassFilter implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new LibraryClassFilter. + * @param classVisitor the ClassVisitor to which visits + * will be delegated. + */ + public LibraryClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Don't delegate visits to program classes. + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + classVisitor.visitLibraryClass(libraryClass); + } +} diff --git a/src/proguard/classfile/visitor/LibraryMemberFilter.java b/src/proguard/classfile/visitor/LibraryMemberFilter.java new file mode 100644 index 000000000..eae06982a --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * MemberVisitor, 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 MemberVisitor 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/MemberAccessFilter.java b/src/proguard/classfile/visitor/MemberAccessFilter.java new file mode 100644 index 000000000..6bdc15241 --- /dev/null +++ b/src/proguard/classfile/visitor/MemberAccessFilter.java @@ -0,0 +1,122 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * MemberVisitor, but only when the visited member has the proper + * access flags. + *

+ * If conflicting access flags (public/private/protected) are specified, + * having one of them set will be considered sufficient. + * + * @see ClassConstants + * + * @author Eric Lafortune + */ +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 + // of them being set is sufficient. + private static final int ACCESS_MASK = + ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_PROTECTED; + + private final int requiredSetAccessFlags; + private final int requiredUnsetAccessFlags; + private final int requiredOneSetAccessFlags; + private final MemberVisitor memberVisitor; + + + /** + * 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 memberVisitor the MemberVisitor to + * which visits will be delegated. + */ + public MemberAccessFilter(int requiredSetAccessFlags, + int requiredUnsetAccessFlags, + MemberVisitor memberVisitor) + { + this.requiredSetAccessFlags = requiredSetAccessFlags & ~ACCESS_MASK; + this.requiredUnsetAccessFlags = requiredUnsetAccessFlags; + this.requiredOneSetAccessFlags = requiredSetAccessFlags & ACCESS_MASK; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (accepted(programField.getAccessFlags())) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (accepted(programMethod.getAccessFlags())) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (accepted(libraryField.getAccessFlags())) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (accepted(libraryMethod.getAccessFlags())) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } + + + // Small utility methods. + + private boolean accepted(int accessFlags) + { + return (requiredSetAccessFlags & ~accessFlags) == 0 && + (requiredUnsetAccessFlags & accessFlags) == 0 && + (requiredOneSetAccessFlags == 0 || + (requiredOneSetAccessFlags & accessFlags) != 0); + } +} diff --git a/src/proguard/classfile/visitor/MemberClassAccessFilter.java b/src/proguard/classfile/visitor/MemberClassAccessFilter.java new file mode 100644 index 000000000..3605407aa --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 MemberVisitor delegates its visits to another given + * MemberVisitor, 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 MemberVisitor 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/MemberCollector.java b/src/proguard/classfile/visitor/MemberCollector.java new file mode 100644 index 000000000..46665f8f5 --- /dev/null +++ b/src/proguard/classfile/visitor/MemberCollector.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; + +import java.util.Set; + +/** + * This MemberVisitor collects the concatenated name/descriptor strings of + * class members that have been visited. + * + * @author Eric Lafortune + */ +public class MemberCollector +extends SimplifiedVisitor +implements MemberVisitor +{ + private final Set set; + + + /** + * Creates a new MemberCollector. + * @param set the Set in which all method names/descriptor + * strings will be collected. + */ + public MemberCollector(Set set) + { + this.set = set; + } + + + // Implementations for MemberVisitor. + + + public void visitAnyMember(Clazz clazz, Member member) + { + set.add(member.getName(clazz) + member.getDescriptor(clazz)); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/MemberCounter.java b/src/proguard/classfile/visitor/MemberCounter.java new file mode 100644 index 000000000..58df4a75c --- /dev/null +++ b/src/proguard/classfile/visitor/MemberCounter.java @@ -0,0 +1,72 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 counts the number of class members that have been visited. + * + * @author Eric Lafortune + */ +public class MemberCounter implements MemberVisitor +{ + private int count; + + + /** + * Returns the number of class members that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for MemberVisitor. + + public void visitLibraryField(LibraryClass libraryClass, + LibraryField libraryField) + { + count++; + } + + + public void visitLibraryMethod(LibraryClass libraryClass, + LibraryMethod libraryMethod) + { + count++; + } + + + public void visitProgramField(ProgramClass programClass, + ProgramField programField) + { + count++; + } + + + 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 000000000..cce515a05 --- /dev/null +++ b/src/proguard/classfile/visitor/MemberDescriptorFilter.java @@ -0,0 +1,113 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 MemberVisitor delegates its visits to another given + * MemberVisitor, 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 MemberVisitor to which visits + * will be delegated. + */ + public MemberDescriptorFilter(String regularExpression, + MemberVisitor memberVisitor) + { + this(new ClassNameParser().parse(regularExpression), memberVisitor); + } + + + /** + * Creates a new MemberDescriptorFilter. + * @param regularExpressionMatcher the regular expression against which + * member descriptors will be matched. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. + */ + public MemberDescriptorFilter(StringMatcher regularExpressionMatcher, + MemberVisitor memberVisitor) + { + this.regularExpressionMatcher = regularExpressionMatcher; + 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/MemberNameFilter.java b/src/proguard/classfile/visitor/MemberNameFilter.java new file mode 100644 index 000000000..9996a4e62 --- /dev/null +++ b/src/proguard/classfile/visitor/MemberNameFilter.java @@ -0,0 +1,114 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 MemberVisitor delegates its visits to another given + * MemberVisitor, 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 MemberVisitor to which visits + * will be delegated. + */ + public MemberNameFilter(String regularExpression, + MemberVisitor memberVisitor) + { + this(new ListParser(new NameParser()).parse(regularExpression), + memberVisitor); + } + + + /** + * Creates a new MemberNameFilter. + * @param regularExpressionMatcher the regular expression against which + * member names will be matched. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. + */ + public MemberNameFilter(StringMatcher regularExpressionMatcher, + MemberVisitor memberVisitor) + { + this.regularExpressionMatcher = regularExpressionMatcher; + 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 000000000..e82e52ff4 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/MemberVisitor.java b/src/proguard/classfile/visitor/MemberVisitor.java new file mode 100644 index 000000000..7b45662b2 --- /dev/null +++ b/src/proguard/classfile/visitor/MemberVisitor.java @@ -0,0 +1,40 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 + * ProgramMember objects and LibraryMember + * objects. + * + * @author Eric Lafortune + */ +public interface MemberVisitor +{ + 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 new file mode 100644 index 000000000..893a6994d --- /dev/null +++ b/src/proguard/classfile/visitor/MethodImplementationFilter.java @@ -0,0 +1,70 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; + +/** + * This MemberVisitor delegates its visits to methods to + * another given MemberVisitor, but only when the visited + * method may have implementations. + * + * @see Clazz#mayHaveImplementations(Method) + * @author Eric Lafortune + */ +public class MethodImplementationFilter +extends SimplifiedVisitor +implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MethodImplementationFilter. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. + */ + public MethodImplementationFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (programClass.mayHaveImplementations(programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (libraryClass.mayHaveImplementations(libraryMethod)) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } +} diff --git a/src/proguard/classfile/visitor/MethodImplementationTraveler.java b/src/proguard/classfile/visitor/MethodImplementationTraveler.java new file mode 100644 index 000000000..c9f942ecd --- /dev/null +++ b/src/proguard/classfile/visitor/MethodImplementationTraveler.java @@ -0,0 +1,128 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; + +/** + * This MemberVisitor lets a given MemberVisitor + * travel to all concrete and abstract implementations of the visited methods + * in their class hierarchies. + * + * @author Eric Lafortune + */ +public class MethodImplementationTraveler +extends SimplifiedVisitor +implements MemberVisitor +{ + private final boolean visitThisMethod; + private final boolean visitSuperMethods; + private final boolean visitInterfaceMethods; + private final boolean visitOverridingMethods; + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MethodImplementationTraveler. + * @param visitThisMethod specifies whether to visit the originally + * visited methods. + * @param visitSuperMethods specifies whether to visit the method in + * the super classes. + * @param visitInterfaceMethods specifies whether to visit the method in + * the interface classes. + * @param visitOverridingMethods specifies whether to visit the method in + * the subclasses. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. + */ + public MethodImplementationTraveler(boolean visitThisMethod, + boolean visitSuperMethods, + boolean visitInterfaceMethods, + boolean visitOverridingMethods, + MemberVisitor memberVisitor) + { + this.visitThisMethod = visitThisMethod; + this.visitSuperMethods = visitSuperMethods; + this.visitInterfaceMethods = visitInterfaceMethods; + this.visitOverridingMethods = visitOverridingMethods; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (visitThisMethod) + { + programMethod.accept(programClass, memberVisitor); + } + + if (!isSpecial(programClass, programMethod)) + { + programClass.hierarchyAccept(false, + visitSuperMethods, + visitInterfaceMethods, + visitOverridingMethods, + new NamedMethodVisitor(programMethod.getName(programClass), + programMethod.getDescriptor(programClass), + new MemberAccessFilter(0, + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC, + memberVisitor))); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (visitThisMethod) + { + libraryMethod.accept(libraryClass, memberVisitor); + } + + if (!isSpecial(libraryClass, libraryMethod)) + { + libraryClass.hierarchyAccept(false, + visitSuperMethods, + visitInterfaceMethods, + visitOverridingMethods, + new NamedMethodVisitor(libraryMethod.getName(libraryClass), + libraryMethod.getDescriptor(libraryClass), + new MemberAccessFilter(0, + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC, + memberVisitor))); + } + } + + + // Small utility methods. + + private boolean isSpecial(Clazz clazz, Method method) + { + return (method.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC)) != 0 || + method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT); + } +} diff --git a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java new file mode 100644 index 000000000..0e96cf1e1 --- /dev/null +++ b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java @@ -0,0 +1,88 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; + + +/** + * This ClassPoolVisitor delegates all visits to each ClassPoolVisitor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiClassPoolVisitor implements ClassPoolVisitor +{ + private static final int ARRAY_SIZE_INCREMENT = 5; + + private ClassPoolVisitor[] classPoolVisitors; + private int classPoolVisitorCount; + + + public MultiClassPoolVisitor() + { + } + + + public MultiClassPoolVisitor(ClassPoolVisitor[] classPoolVisitors) + { + this.classPoolVisitors = classPoolVisitors; + this.classPoolVisitorCount = classPoolVisitors.length; + } + + + public void addClassPoolVisitor(ClassPoolVisitor classPoolVisitor) + { + ensureArraySize(); + + classPoolVisitors[classPoolVisitorCount++] = classPoolVisitor; + } + + + private void ensureArraySize() + { + if (classPoolVisitors == null) + { + classPoolVisitors = new ClassPoolVisitor[ARRAY_SIZE_INCREMENT]; + } + else if (classPoolVisitors.length == classPoolVisitorCount) + { + ClassPoolVisitor[] newClassPoolVisitors = + new ClassPoolVisitor[classPoolVisitorCount + + ARRAY_SIZE_INCREMENT]; + System.arraycopy(classPoolVisitors, 0, + newClassPoolVisitors, 0, + classPoolVisitorCount); + classPoolVisitors = newClassPoolVisitors; + } + } + + + // Implementations for ClassPoolVisitor. + + public void visitClassPool(ClassPool classPool) + { + for (int index = 0; index < classPoolVisitorCount; index++) + { + 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 000000000..059e9b720 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/MultiMemberVisitor.java b/src/proguard/classfile/visitor/MultiMemberVisitor.java new file mode 100644 index 000000000..800d65ffd --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/NamedClassVisitor.java b/src/proguard/classfile/visitor/NamedClassVisitor.java new file mode 100644 index 000000000..79e14c956 --- /dev/null +++ b/src/proguard/classfile/visitor/NamedClassVisitor.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassPool; + + +/** + * This class visits Clazz objects with the given name. + * + * @author Eric Lafortune + */ +public class NamedClassVisitor implements ClassPoolVisitor +{ + private final ClassVisitor classVisitor; + private final String name; + + + public NamedClassVisitor(ClassVisitor classVisitor, + String name) + { + this.classVisitor = classVisitor; + this.name = name; + } + + + public void visitClassPool(ClassPool classPool) + { + classPool.classAccept(name, classVisitor); + } +} diff --git a/src/proguard/classfile/visitor/NamedFieldVisitor.java b/src/proguard/classfile/visitor/NamedFieldVisitor.java new file mode 100644 index 000000000..685f62d54 --- /dev/null +++ b/src/proguard/classfile/visitor/NamedFieldVisitor.java @@ -0,0 +1,61 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 class visits ProgramMember objects referring to fields, identified by + * a name and descriptor pair. + * + * @author Eric Lafortune + */ +public class NamedFieldVisitor implements ClassVisitor +{ + private final String name; + private final String descriptor; + private final MemberVisitor memberVisitor; + + + public NamedFieldVisitor(String name, + String descriptor, + MemberVisitor memberVisitor) + { + this.name = name; + this.descriptor = descriptor; + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.fieldAccept(name, descriptor, memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.fieldAccept(name, descriptor, memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/NamedMethodVisitor.java b/src/proguard/classfile/visitor/NamedMethodVisitor.java new file mode 100644 index 000000000..c2baf193c --- /dev/null +++ b/src/proguard/classfile/visitor/NamedMethodVisitor.java @@ -0,0 +1,61 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 class visits ProgramMember objects referring to methods, identified by + * a name and descriptor pair. + * + * @author Eric Lafortune + */ +public class NamedMethodVisitor implements ClassVisitor +{ + private final String name; + private final String descriptor; + private final MemberVisitor memberVisitor; + + + public NamedMethodVisitor(String name, + String descriptor, + MemberVisitor memberVisitor) + { + this.name = name; + this.descriptor = descriptor; + this.memberVisitor = memberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.methodAccept(name, descriptor, memberVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.methodAccept(name, descriptor, memberVisitor); + } +} diff --git a/src/proguard/classfile/visitor/ProgramClassFilter.java b/src/proguard/classfile/visitor/ProgramClassFilter.java new file mode 100644 index 000000000..976658c43 --- /dev/null +++ b/src/proguard/classfile/visitor/ProgramClassFilter.java @@ -0,0 +1,60 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * ClassVisitor, but only when visiting program classes. + * + * @author Eric Lafortune + */ +public class ProgramClassFilter implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ProgramClassFilter. + * @param classVisitor the ClassVisitor to which visits + * will be delegated. + */ + public ProgramClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + classVisitor.visitProgramClass(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // 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 000000000..cf187fbfa --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * MemberVisitor, 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 MemberVisitor 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/ReferencedClassVisitor.java b/src/proguard/classfile/visitor/ReferencedClassVisitor.java new file mode 100644 index 000000000..e7fe855d4 --- /dev/null +++ b/src/proguard/classfile/visitor/ReferencedClassVisitor.java @@ -0,0 +1,255 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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. Only downstream elements are considered (in order to avoid + * loops and repeated 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 superclass and interfaces. + libraryClass.superClassAccept(classVisitor); + libraryClass.interfacesAccept(classVisitor); + + // 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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // Let the visitor visit the class referenced in the reference constant. + invokeDynamicConstant.referencedClassesAccept(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 000000000..3c590757c --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/SimilarMemberVisitor.java b/src/proguard/classfile/visitor/SimilarMemberVisitor.java new file mode 100644 index 000000000..5087f4892 --- /dev/null +++ b/src/proguard/classfile/visitor/SimilarMemberVisitor.java @@ -0,0 +1,125 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 lets a given MemberVisitor + * visit all members that have the same name and type as the visited methods + * in the class hierarchy of a given target class. + * + * @author Eric Lafortune + */ +public class SimilarMemberVisitor +implements MemberVisitor +{ + private final Clazz targetClass; + private final boolean visitThisMember; + private final boolean visitSuperMembers; + private final boolean visitInterfaceMembers; + private final boolean visitOverridingMembers; + private final MemberVisitor memberVisitor; + + + /** + * Creates a new SimilarMemberVisitor. + * @param targetClass the class in whose hierarchy to look for + * the visited class members. + * @param visitThisMember specifies whether to visit the class + * members in the target class itself. + * @param visitSuperMembers specifies whether to visit the class + * members in the super classes of the target + * class. + * @param visitInterfaceMembers specifies whether to visit the class + * members in the interface classes of the + * target class. + * @param visitOverridingMembers specifies whether to visit the class + * members in the subclasses of the target + * class. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. + */ + public SimilarMemberVisitor(Clazz targetClass, + boolean visitThisMember, + boolean visitSuperMembers, + boolean visitInterfaceMembers, + boolean visitOverridingMembers, + MemberVisitor memberVisitor) + { + this.targetClass = targetClass; + this.visitThisMember = visitThisMember; + this.visitSuperMembers = visitSuperMembers; + this.visitInterfaceMembers = visitInterfaceMembers; + this.visitOverridingMembers = visitOverridingMembers; + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + targetClass.hierarchyAccept(visitThisMember, + visitSuperMembers, + visitInterfaceMembers, + visitOverridingMembers, + new NamedFieldVisitor(programField.getName(programClass), + programField.getDescriptor(programClass), + memberVisitor)); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + targetClass.hierarchyAccept(visitThisMember, + visitSuperMembers, + visitInterfaceMembers, + visitOverridingMembers, + new NamedFieldVisitor(libraryField.getName(libraryClass), + libraryField.getDescriptor(libraryClass), + memberVisitor)); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + targetClass.hierarchyAccept(visitThisMember, + visitSuperMembers, + visitInterfaceMembers, + visitOverridingMembers, + new NamedMethodVisitor(programMethod.getName(programClass), + programMethod.getDescriptor(programClass), + memberVisitor)); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + targetClass.hierarchyAccept(visitThisMember, + visitSuperMembers, + visitInterfaceMembers, + visitOverridingMembers, + new NamedMethodVisitor(libraryMethod.getName(libraryClass), + libraryMethod.getDescriptor(libraryClass), + memberVisitor)); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/SimpleClassPrinter.java b/src/proguard/classfile/visitor/SimpleClassPrinter.java new file mode 100644 index 000000000..df630c7ea --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ClassVisitor and MemberVisitor + * 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 + * System.out, including the access modifiers. + */ + public SimpleClassPrinter() + { + this(true); + } + + /** + * Creates a new SimpleClassPrinter that prints to + * System.out, with or without the access modifiers. + */ + public SimpleClassPrinter(boolean printAccessModifiers) + { + this(printAccessModifiers, System.out); + } + + /** + * Creates a new SimpleClassPrinter that prints to the given + * PrintStream, 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/SubclassFilter.java b/src/proguard/classfile/visitor/SubclassFilter.java new file mode 100644 index 000000000..6b6f84b98 --- /dev/null +++ b/src/proguard/classfile/visitor/SubclassFilter.java @@ -0,0 +1,91 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 its visits to another given + * ClassVisitor, except for classes that have a given class as + * direct subclass. + * + * @author Eric Lafortune + */ +public class SubclassFilter implements ClassVisitor +{ + private final Clazz subclass; + private final ClassVisitor classVisitor; + + + /** + * Creates a new SubclassFilter. + * @param subclass the class whose superclasses will not be visited. + * @param classVisitor the ClassVisitor to which visits will + * be delegated. + */ + public SubclassFilter(Clazz subclass, + ClassVisitor classVisitor) + { + this.subclass = subclass; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!present(programClass.subClasses)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (!present(libraryClass.subClasses)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private boolean present(Clazz[] subclasses) + { + if (subclasses == null) + { + return false; + } + + for (int index = 0; index < subclasses.length; index++) + { + if (subclasses[index].equals(subclass)) + { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/SubclassTraveler.java b/src/proguard/classfile/visitor/SubclassTraveler.java new file mode 100644 index 000000000..32dccb72a --- /dev/null +++ b/src/proguard/classfile/visitor/SubclassTraveler.java @@ -0,0 +1,60 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 lets a given ClassVisitor + * travel to direct subclasses of the visited class. + * + * @author Eric Lafortune + */ +public class SubclassTraveler implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ClassHierarchyTraveler. + * @param classVisitor the ClassVisitor to + * which visits will be delegated. + */ + public SubclassTraveler(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.subclassesAccept(classVisitor); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + libraryClass.subclassesAccept(classVisitor); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/VariableClassVisitor.java b/src/proguard/classfile/visitor/VariableClassVisitor.java new file mode 100644 index 000000000..17a55227b --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/VariableMemberVisitor.java b/src/proguard/classfile/visitor/VariableMemberVisitor.java new file mode 100644 index 000000000..34c39f377 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/classfile/visitor/package.html b/src/proguard/classfile/visitor/package.html new file mode 100644 index 000000000..d3be40c09 --- /dev/null +++ b/src/proguard/classfile/visitor/package.html @@ -0,0 +1,40 @@ + +This package contains interfaces and classes for processing class files from +the {@link proguard.classfile proguard.classfile} package using +the visitor pattern. Cfr., for instance, "Design Patterns, Elements of +Reusable OO Software", by Gamma, Helm, Johnson, and Vlissider. +

+Why the visitor pattern? Class files frequently contain lists of elements of +various mixed types: class items, constant pool entries, attributes,... +These lists and types are largely fixed; they won't change much in future +releases of the Java class file specifications. On the other hand, the kinds +of operations that we may wish to perform on the class files may change and +expand. We want to separate the objects and the operations performed upon them. +This is a good place to use the visitor pattern. +

+Visitor interfaces avoid having to do series of instanceof tests +on the elements of a list, followed by type casts and the proper operations. +Every list element is a visitor accepter. When its accept method +is called by a visitor, it calls its corresponding visitX method +in the visitor, passing itself as an argument. This technique is called +double-dispatch. +

+As already mentioned, the main advantage is avoiding lots of +instanceof tests and type casts. Also, implementing a visitor +interface ensures you're handling all possible visitor accepter types. Each +type has its own method, which you simply have to implement. +

+A disadvantage is that the visitor methods always get the same names, specified +by the visitor interface. These names aren't descriptive at all, making code +harder to read. It's the visitor classes that describe the operations now. +

+Also, the visitor methods always have the same parameters and return values, as +specified by the visitor interfaces. Passing additional parameters is done by +means of extra fields in the visitor, which is somewhat of a kludge. +

+Because objects (the visitor accepters) and the operations performed upon them +(the visitors) are now separated, it becomes harder to associate some state +with the objects. For convenience, we always provide an extra visitor +info field in visitor accepters, in which visitors can put any temporary +information they want. + diff --git a/src/proguard/evaluation/BasicBranchUnit.java b/src/proguard/evaluation/BasicBranchUnit.java new file mode 100644 index 000000000..127e92241 --- /dev/null +++ b/src/proguard/evaluation/BasicBranchUnit.java @@ -0,0 +1,126 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..474556c2f --- /dev/null +++ b/src/proguard/evaluation/BasicInvocationUnit.java @@ -0,0 +1,425 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 +{ + protected final ValueFactory valueFactory; + + // Fields acting as parameters between the visitor methods. + private boolean isStatic; + private boolean isLoad; + private Stack stack; + private Clazz returnTypeClass; + + + /** + * Creates a new BasicInvocationUnit with the given value factory. + */ + public BasicInvocationUnit(ValueFactory valueFactory) + { + this.valueFactory = valueFactory; + } + + + // Implementations for InvocationUnit. + + 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, isStatic); + + // 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++; + } + } + } + + + public void exitMethod(Clazz clazz, Method method, Value returnValue) + { + setMethodReturnValue(clazz, method, returnValue); + } + + + 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: + case InstructionConstants.OP_INVOKEDYNAMIC: + 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()); + } + + // 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)); + } + } + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + String type = invokeDynamicConstant.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--) + { + stack.pop(); + } + + // Push the return value, if applicable. + String returnType = ClassUtil.internalMethodReturnType(type); + if (returnType.charAt(0) != ClassConstants.INTERNAL_TYPE_VOID) + { + stack.push(getMethodReturnValue(clazz, invokeDynamicConstant, returnType)); + } + } + + + /** + * Sets the class through which the specified field is accessed. + */ + protected void setFieldClassValue(Clazz clazz, + RefConstant refConstant, + ReferenceValue value) + { + // We don't care about the new value. + } + + + /** + * Returns the class though which the specified field is accessed. + */ + 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); + } + + + /** + * Sets the value of the specified field. + */ + protected void setFieldValue(Clazz clazz, + RefConstant refConstant, + Value value) + { + // We don't care about the new field value. + } + + + /** + * Returns the value of the specified field. + */ + 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); + } + + + /** + * Sets the value of the specified method parameter. + */ + protected void setMethodParameterValue(Clazz clazz, + RefConstant refConstant, + int parameterIndex, + Value value) + { + // We don't care about the parameter value. + } + + + /** + * Returns the value of the specified method parameter. + */ + protected Value getMethodParameterValue(Clazz clazz, + Method method, + int parameterIndex, + String type, + Clazz referencedClass) + { + return valueFactory.createValue(type, referencedClass, true); + } + + + /** + * Sets the return value of the specified method. + */ + protected void setMethodReturnValue(Clazz clazz, + Method method, + Value value) + { + // We don't care about the return value. + } + + + /** + * Returns the return value of the specified method. + */ + 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); + } + + + /** + * Returns the return value of the specified method. + */ + protected Value getMethodReturnValue(Clazz clazz, + InvokeDynamicConstant invokeDynamicConstant, + String type) + { + // Try to figure out the class of the return type. + Clazz[] referencedClasses = invokeDynamicConstant.referencedClasses; + + Clazz returnTypeClass = referencedClasses == null ? null : + referencedClasses[referencedClasses.length - 1]; + + 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/evaluation/BranchUnit.java b/src/proguard/evaluation/BranchUnit.java new file mode 100644 index 000000000..a381da760 --- /dev/null +++ b/src/proguard/evaluation/BranchUnit.java @@ -0,0 +1,63 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This InstructionVisitor evaluates the instructions that it visits. + * + * @author Eric Lafortune + */ +public interface BranchUnit +{ + /** + * Sets the new instruction offset. + */ + 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(Clazz clazz, + CodeAttribute codeAttribute, + int offset, + int branchTarget, + int conditional); + + + /** + * Returns from the method with the given value. + */ + public void returnFromMethod(); + + + /** + * Handles the throwing of an exception. + */ + public void throwException(); +} diff --git a/src/proguard/evaluation/ClassConstantValueFactory.java b/src/proguard/evaluation/ClassConstantValueFactory.java new file mode 100644 index 000000000..1c418c288 --- /dev/null +++ b/src/proguard/evaluation/ClassConstantValueFactory.java @@ -0,0 +1,53 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.evaluation.value.*; + +/** + * This class creates java.lang.Class ReferenceValue instances that correspond + * to specified constant pool entries. + * + * @author Eric Lafortune + */ +public class ClassConstantValueFactory +extends ConstantValueFactory +{ + public ClassConstantValueFactory(ValueFactory valueFactory) + { + super(valueFactory); + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Create a Class reference instead of a reference to the class. + value = valueFactory.createReferenceValue(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS, + classConstant.javaLangClassClass, + false); + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/ConstantValueFactory.java b/src/proguard/evaluation/ConstantValueFactory.java new file mode 100644 index 000000000..0afb20c1b --- /dev/null +++ b/src/proguard/evaluation/ConstantValueFactory.java @@ -0,0 +1,113 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.evaluation.value.*; + +/** + * This class creates Value instance that correspond to specified constant pool + * entries. + * + * @author Eric Lafortune + */ +public class ConstantValueFactory +extends SimplifiedVisitor +implements ConstantVisitor +{ + protected final ValueFactory valueFactory; + + // Field acting as a parameter for the ConstantVisitor methods. + protected Value value; + + + public ConstantValueFactory(ValueFactory valueFactory) + { + this.valueFactory = valueFactory; + } + + + /** + * Returns the Value of the constant pool element at the given index. + */ + public Value constantValue(Clazz clazz, + int constantIndex) + { + // Visit the constant pool entry to get its return value. + clazz.constantPoolEntryAccept(constantIndex, this); + + return value; + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + value = valueFactory.createIntegerValue(integerConstant.getValue()); + } + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + value = valueFactory.createLongValue(longConstant.getValue()); + } + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + value = valueFactory.createFloatValue(floatConstant.getValue()); + } + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + value = valueFactory.createDoubleValue(doubleConstant.getValue()); + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + value = valueFactory.createReferenceValue(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING, + stringConstant.javaLangStringClass, + false); + } + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + value = valueFactory.createReferenceValue(ClassConstants.INTERNAL_NAME_JAVA_LANG_INVOKE_METHOD_HANDLE, + methodHandleConstant.javaLangInvokeMethodHandleClass, + false); + } + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + value = valueFactory.createReferenceValue(classConstant.getName(clazz), + classConstant.referencedClass, + false); + } + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + value = valueFactory.createReferenceValue(ClassConstants.INTERNAL_NAME_JAVA_LANG_INVOKE_METHOD_TYPE, + methodTypeConstant.javaLangInvokeMethodTypeClass, + false); + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/InvocationUnit.java b/src/proguard/evaluation/InvocationUnit.java new file mode 100644 index 000000000..e526b356d --- /dev/null +++ b/src/proguard/evaluation/InvocationUnit.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/Processor.java b/src/proguard/evaluation/Processor.java new file mode 100644 index 000000000..3bfc5f373 --- /dev/null +++ b/src/proguard/evaluation/Processor.java @@ -0,0 +1,908 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.evaluation.value.*; + +/** + * This InstructionVisitor executes the instructions that it visits on a given + * local variable frame and stack. + * + * @author Eric Lafortune + */ +public class Processor +implements InstructionVisitor +{ + private final Variables variables; + private final Stack stack; + private final ValueFactory valueFactory; + private final BranchUnit branchUnit; + private final InvocationUnit invocationUnit; + + private final ConstantValueFactory constantValueFactory; + private final ClassConstantValueFactory classConstantValueFactory; + + + /** + * 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 invocationUnit the class that can access other program members. + */ + public Processor(Variables variables, + Stack stack, + ValueFactory valueFactory, + BranchUnit branchUnit, + InvocationUnit invocationUnit) + { + this.variables = variables; + this.stack = stack; + this.valueFactory = valueFactory; + this.branchUnit = branchUnit; + this.invocationUnit = invocationUnit; + + constantValueFactory = new ConstantValueFactory(valueFactory); + classConstantValueFactory = new ClassConstantValueFactory(valueFactory); + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + case InstructionConstants.OP_NOP: + break; + + case InstructionConstants.OP_ACONST_NULL: + stack.push(valueFactory.createReferenceValueNull()); + break; + + 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: + stack.push(valueFactory.createIntegerValue(simpleInstruction.constant)); + break; + + case InstructionConstants.OP_LCONST_0: + case InstructionConstants.OP_LCONST_1: + stack.push(valueFactory.createLongValue(simpleInstruction.constant)); + break; + + case InstructionConstants.OP_FCONST_0: + case InstructionConstants.OP_FCONST_1: + case InstructionConstants.OP_FCONST_2: + stack.push(valueFactory.createFloatValue((float)simpleInstruction.constant)); + break; + + case InstructionConstants.OP_DCONST_0: + case InstructionConstants.OP_DCONST_1: + stack.push(valueFactory.createDoubleValue((double)simpleInstruction.constant)); + break; + + case InstructionConstants.OP_IALOAD: + case InstructionConstants.OP_BALOAD: + case InstructionConstants.OP_CALOAD: + case InstructionConstants.OP_SALOAD: + stack.ipop(); + stack.apop(); + stack.push(valueFactory.createIntegerValue()); + break; + + case InstructionConstants.OP_LALOAD: + stack.ipop(); + stack.apop(); + stack.push(valueFactory.createLongValue()); + break; + + case InstructionConstants.OP_FALOAD: + stack.ipop(); + stack.apop(); + stack.push(valueFactory.createFloatValue()); + break; + + case InstructionConstants.OP_DALOAD: + stack.ipop(); + stack.apop(); + stack.push(valueFactory.createDoubleValue()); + break; + + case InstructionConstants.OP_AALOAD: + { + IntegerValue arrayIndex = stack.ipop(); + ReferenceValue arrayReference = stack.apop(); + stack.push(arrayReference.arrayLoad(arrayIndex, valueFactory)); + break; + } + + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + stack.ipop(); + stack.ipop(); + stack.apop(); + break; + + case InstructionConstants.OP_LASTORE: + stack.lpop(); + stack.ipop(); + stack.apop(); + break; + + case InstructionConstants.OP_FASTORE: + stack.fpop(); + stack.ipop(); + stack.apop(); + break; + + case InstructionConstants.OP_DASTORE: + stack.dpop(); + stack.ipop(); + stack.apop(); + break; + + case InstructionConstants.OP_AASTORE: + stack.apop(); + stack.ipop(); + stack.apop(); + break; + + case InstructionConstants.OP_POP: + stack.pop1(); + break; + + case InstructionConstants.OP_POP2: + stack.pop2(); + break; + + case InstructionConstants.OP_DUP: + stack.dup(); + break; + + case InstructionConstants.OP_DUP_X1: + stack.dup_x1(); + break; + + case InstructionConstants.OP_DUP_X2: + stack.dup_x2(); + break; + + case InstructionConstants.OP_DUP2: + stack.dup2(); + break; + + case InstructionConstants.OP_DUP2_X1: + stack.dup2_x1(); + break; + + case InstructionConstants.OP_DUP2_X2: + stack.dup2_x2(); + break; + + case InstructionConstants.OP_SWAP: + stack.swap(); + break; + + case InstructionConstants.OP_IADD: + stack.push(stack.ipop().add(stack.ipop())); + break; + + case InstructionConstants.OP_LADD: + stack.push(stack.lpop().add(stack.lpop())); + break; + + case InstructionConstants.OP_FADD: + stack.push(stack.fpop().add(stack.fpop())); + break; + + case InstructionConstants.OP_DADD: + stack.push(stack.dpop().add(stack.dpop())); + break; + + case InstructionConstants.OP_ISUB: + stack.push(stack.ipop().subtractFrom(stack.ipop())); + break; + + case InstructionConstants.OP_LSUB: + stack.push(stack.lpop().subtractFrom(stack.lpop())); + break; + + case InstructionConstants.OP_FSUB: + stack.push(stack.fpop().subtractFrom(stack.fpop())); + break; + + case InstructionConstants.OP_DSUB: + stack.push(stack.dpop().subtractFrom(stack.dpop())); + break; + + case InstructionConstants.OP_IMUL: + stack.push(stack.ipop().multiply(stack.ipop())); + break; + + case InstructionConstants.OP_LMUL: + stack.push(stack.lpop().multiply(stack.lpop())); + break; + + case InstructionConstants.OP_FMUL: + stack.push(stack.fpop().multiply(stack.fpop())); + break; + + case InstructionConstants.OP_DMUL: + stack.push(stack.dpop().multiply(stack.dpop())); + break; + + case InstructionConstants.OP_IDIV: + try + { + stack.push(stack.ipop().divideOf(stack.ipop())); + } + catch (ArithmeticException ex) + { + stack.push(valueFactory.createIntegerValue()); + // TODO: Forward ArithmeticExceptions. + //stack.clear(); + //stack.push(valueFactory.createReference(false)); + //branchUnit.throwException(); + } + break; + + case InstructionConstants.OP_LDIV: + try + { + stack.push(stack.lpop().divideOf(stack.lpop())); + } + catch (ArithmeticException ex) + { + stack.push(valueFactory.createLongValue()); + // TODO: Forward ArithmeticExceptions. + //stack.clear(); + //stack.push(valueFactory.createReference(false)); + //branchUnit.throwException(); + } + break; + + case InstructionConstants.OP_FDIV: + stack.push(stack.fpop().divideOf(stack.fpop())); + break; + + case InstructionConstants.OP_DDIV: + stack.push(stack.dpop().divideOf(stack.dpop())); + break; + + case InstructionConstants.OP_IREM: + try + { + stack.push(stack.ipop().remainderOf(stack.ipop())); + } + catch (ArithmeticException ex) + { + stack.push(valueFactory.createIntegerValue()); + // TODO: Forward ArithmeticExceptions. + //stack.clear(); + //stack.push(valueFactory.createReference(false)); + //branchUnit.throwException(); + } + break; + + case InstructionConstants.OP_LREM: + try + { + stack.push(stack.lpop().remainderOf(stack.lpop())); + } + catch (ArithmeticException ex) + { + stack.push(valueFactory.createLongValue()); + // TODO: Forward ArithmeticExceptions. + //stack.clear(); + //stack.push(valueFactory.createReference(false)); + //branchUnit.throwException(); + } + break; + + case InstructionConstants.OP_FREM: + stack.push(stack.fpop().remainderOf(stack.fpop())); + break; + + case InstructionConstants.OP_DREM: + stack.push(stack.dpop().remainderOf(stack.dpop())); + break; + + case InstructionConstants.OP_INEG: + stack.push(stack.ipop().negate()); + break; + + case InstructionConstants.OP_LNEG: + stack.push(stack.lpop().negate()); + break; + + case InstructionConstants.OP_FNEG: + stack.push(stack.fpop().negate()); + break; + + case InstructionConstants.OP_DNEG: + stack.push(stack.dpop().negate()); + break; + + case InstructionConstants.OP_ISHL: + stack.push(stack.ipop().shiftLeftOf(stack.ipop())); + break; + + case InstructionConstants.OP_LSHL: + stack.push(stack.ipop().shiftLeftOf(stack.lpop())); + break; + + case InstructionConstants.OP_ISHR: + stack.push(stack.ipop().shiftRightOf(stack.ipop())); + break; + + case InstructionConstants.OP_LSHR: + stack.push(stack.ipop().shiftRightOf(stack.lpop())); + break; + + case InstructionConstants.OP_IUSHR: + stack.push(stack.ipop().unsignedShiftRightOf(stack.ipop())); + break; + + case InstructionConstants.OP_LUSHR: + stack.push(stack.ipop().unsignedShiftRightOf(stack.lpop())); + break; + + case InstructionConstants.OP_IAND: + stack.push(stack.ipop().and(stack.ipop())); + break; + + case InstructionConstants.OP_LAND: + stack.push(stack.lpop().and(stack.lpop())); + break; + + case InstructionConstants.OP_IOR: + stack.push(stack.ipop().or(stack.ipop())); + break; + + case InstructionConstants.OP_LOR: + stack.push(stack.lpop().or(stack.lpop())); + break; + + case InstructionConstants.OP_IXOR: + stack.push(stack.ipop().xor(stack.ipop())); + break; + + case InstructionConstants.OP_LXOR: + stack.push(stack.lpop().xor(stack.lpop())); + break; + + case InstructionConstants.OP_I2L: + stack.push(stack.ipop().convertToLong()); + break; + + case InstructionConstants.OP_I2F: + stack.push(stack.ipop().convertToFloat()); + break; + + case InstructionConstants.OP_I2D: + stack.push(stack.ipop().convertToDouble()); + break; + + case InstructionConstants.OP_L2I: + stack.push(stack.lpop().convertToInteger()); + break; + + case InstructionConstants.OP_L2F: + stack.push(stack.lpop().convertToFloat()); + break; + + case InstructionConstants.OP_L2D: + stack.push(stack.lpop().convertToDouble()); + break; + + case InstructionConstants.OP_F2I: + stack.push(stack.fpop().convertToInteger()); + break; + + case InstructionConstants.OP_F2L: + stack.push(stack.fpop().convertToLong()); + break; + + case InstructionConstants.OP_F2D: + stack.push(stack.fpop().convertToDouble()); + break; + + case InstructionConstants.OP_D2I: + stack.push(stack.dpop().convertToInteger()); + break; + + case InstructionConstants.OP_D2L: + stack.push(stack.dpop().convertToLong()); + break; + + case InstructionConstants.OP_D2F: + stack.push(stack.dpop().convertToFloat()); + break; + + case InstructionConstants.OP_I2B: + stack.push(stack.ipop().convertToByte()); + break; + + case InstructionConstants.OP_I2C: + stack.push(stack.ipop().convertToCharacter()); + break; + + case InstructionConstants.OP_I2S: + stack.push(stack.ipop().convertToShort()); + break; + + case InstructionConstants.OP_LCMP: +// stack.push(stack.lpop().compareReverse(stack.lpop())); + + LongValue longValue1 = stack.lpop(); + LongValue longValue2 = stack.lpop(); + stack.push(longValue2.compare(longValue1)); + break; + + case InstructionConstants.OP_FCMPL: + FloatValue floatValue1 = stack.fpop(); + FloatValue floatValue2 = stack.fpop(); + stack.push(floatValue2.compare(floatValue1)); + break; + + case InstructionConstants.OP_FCMPG: + stack.push(stack.fpop().compareReverse(stack.fpop())); + break; + + case InstructionConstants.OP_DCMPL: + DoubleValue doubleValue1 = stack.dpop(); + DoubleValue doubleValue2 = stack.dpop(); + stack.push(doubleValue2.compare(doubleValue1)); + break; + + case InstructionConstants.OP_DCMPG: + stack.push(stack.dpop().compareReverse(stack.dpop())); + break; + + case InstructionConstants.OP_IRETURN: + invocationUnit.exitMethod(clazz, method, stack.ipop()); + branchUnit.returnFromMethod(); + break; + + case InstructionConstants.OP_LRETURN: + invocationUnit.exitMethod(clazz, method, stack.lpop()); + branchUnit.returnFromMethod(); + break; + + case InstructionConstants.OP_FRETURN: + invocationUnit.exitMethod(clazz, method, stack.fpop()); + branchUnit.returnFromMethod(); + break; + + case InstructionConstants.OP_DRETURN: + invocationUnit.exitMethod(clazz, method, stack.dpop()); + branchUnit.returnFromMethod(); + break; + + case InstructionConstants.OP_ARETURN: + invocationUnit.exitMethod(clazz, method, stack.apop()); + branchUnit.returnFromMethod(); + break; + + case InstructionConstants.OP_RETURN: + branchUnit.returnFromMethod(); + break; + + case InstructionConstants.OP_NEWARRAY: + 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(valueFactory.createIntegerValue()); + break; + + case InstructionConstants.OP_ATHROW: + ReferenceValue exceptionReferenceValue = stack.apop(); + stack.clear(); + stack.push(exceptionReferenceValue); + branchUnit.throwException(); + break; + + case InstructionConstants.OP_MONITORENTER: + case InstructionConstants.OP_MONITOREXIT: + stack.apop(); + break; + + default: + throw new IllegalArgumentException("Unknown simple instruction ["+simpleInstruction.opcode+"]"); + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + int constantIndex = constantInstruction.constantIndex; + + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_LDC: + case InstructionConstants.OP_LDC_W: + case InstructionConstants.OP_LDC2_W: + stack.push(classConstantValueFactory.constantValue(clazz, constantIndex)); + break; + + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_PUTFIELD: + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + invocationUnit.invokeMember(clazz, method, codeAttribute, offset, constantInstruction, stack); + break; + + case InstructionConstants.OP_NEW: + stack.push(constantValueFactory.constantValue(clazz, constantIndex).referenceValue()); + break; + + case InstructionConstants.OP_ANEWARRAY: + { + ReferenceValue referenceValue = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); + + stack.push(valueFactory.createArrayReferenceValue(referenceValue.internalType(), + referenceValue.getReferencedClass(), + stack.ipop())); + break; + } + + case InstructionConstants.OP_CHECKCAST: + // TODO: Check cast. + ReferenceValue castValue = stack.apop(); + ReferenceValue castResultValue = + castValue.isNull() == Value.ALWAYS ? castValue : + castValue.isNull() == Value.NEVER ? constantValueFactory.constantValue(clazz, constantIndex).referenceValue() : + constantValueFactory.constantValue(clazz, constantIndex).referenceValue().generalize(valueFactory.createReferenceValueNull()); + stack.push(castResultValue); + break; + + case InstructionConstants.OP_INSTANCEOF: + { + ReferenceValue referenceValue = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); + + int instanceOf = stack.apop().instanceOf(referenceValue.getType(), + referenceValue.getReferencedClass()); + + stack.push(instanceOf == Value.NEVER ? valueFactory.createIntegerValue(0) : + instanceOf == Value.ALWAYS ? valueFactory.createIntegerValue(1) : + valueFactory.createIntegerValue()); + break; + } + + case InstructionConstants.OP_MULTIANEWARRAY: + { + int dimensionCount = constantInstruction.constant; + for (int dimension = 0; dimension < dimensionCount; dimension++) + { + // TODO: Use array lengths. + IntegerValue arrayLength = stack.ipop(); + } + + stack.push(constantValueFactory.constantValue(clazz, constantIndex).referenceValue()); + break; + } + + default: + throw new IllegalArgumentException("Unknown constant pool instruction ["+constantInstruction.opcode+"]"); + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + int variableIndex = variableInstruction.variableIndex; + + switch (variableInstruction.opcode) + { + case InstructionConstants.OP_ILOAD: + case InstructionConstants.OP_ILOAD_0: + case InstructionConstants.OP_ILOAD_1: + case InstructionConstants.OP_ILOAD_2: + case InstructionConstants.OP_ILOAD_3: + stack.push(variables.iload(variableIndex)); + break; + + case InstructionConstants.OP_LLOAD: + case InstructionConstants.OP_LLOAD_0: + case InstructionConstants.OP_LLOAD_1: + case InstructionConstants.OP_LLOAD_2: + case InstructionConstants.OP_LLOAD_3: + stack.push(variables.lload(variableIndex)); + break; + + case InstructionConstants.OP_FLOAD: + case InstructionConstants.OP_FLOAD_0: + case InstructionConstants.OP_FLOAD_1: + case InstructionConstants.OP_FLOAD_2: + case InstructionConstants.OP_FLOAD_3: + stack.push(variables.fload(variableIndex)); + break; + + case InstructionConstants.OP_DLOAD: + case InstructionConstants.OP_DLOAD_0: + case InstructionConstants.OP_DLOAD_1: + case InstructionConstants.OP_DLOAD_2: + case InstructionConstants.OP_DLOAD_3: + stack.push(variables.dload(variableIndex)); + break; + + case InstructionConstants.OP_ALOAD: + case InstructionConstants.OP_ALOAD_0: + case InstructionConstants.OP_ALOAD_1: + case InstructionConstants.OP_ALOAD_2: + case InstructionConstants.OP_ALOAD_3: + stack.push(variables.aload(variableIndex)); + break; + + case InstructionConstants.OP_ISTORE: + case InstructionConstants.OP_ISTORE_0: + case InstructionConstants.OP_ISTORE_1: + case InstructionConstants.OP_ISTORE_2: + case InstructionConstants.OP_ISTORE_3: + variables.store(variableIndex, stack.ipop()); + break; + + case InstructionConstants.OP_LSTORE: + case InstructionConstants.OP_LSTORE_0: + case InstructionConstants.OP_LSTORE_1: + case InstructionConstants.OP_LSTORE_2: + case InstructionConstants.OP_LSTORE_3: + variables.store(variableIndex, stack.lpop()); + break; + + case InstructionConstants.OP_FSTORE: + case InstructionConstants.OP_FSTORE_0: + case InstructionConstants.OP_FSTORE_1: + case InstructionConstants.OP_FSTORE_2: + case InstructionConstants.OP_FSTORE_3: + variables.store(variableIndex, stack.fpop()); + break; + + case InstructionConstants.OP_DSTORE: + case InstructionConstants.OP_DSTORE_0: + case InstructionConstants.OP_DSTORE_1: + case InstructionConstants.OP_DSTORE_2: + case InstructionConstants.OP_DSTORE_3: + variables.store(variableIndex, stack.dpop()); + break; + + case InstructionConstants.OP_ASTORE: + case InstructionConstants.OP_ASTORE_0: + case InstructionConstants.OP_ASTORE_1: + case InstructionConstants.OP_ASTORE_2: + case InstructionConstants.OP_ASTORE_3: + // The operand on the stack can be a reference or a return + // address, so we'll relax the pop operation. + //variables.store(variableIndex, stack.apop()); + variables.store(variableIndex, stack.pop()); + break; + + case InstructionConstants.OP_IINC: + variables.store(variableIndex, + variables.iload(variableIndex).add( + valueFactory.createIntegerValue(variableInstruction.constant))); + break; + + case InstructionConstants.OP_RET: + // The return address should be in the last offset of the + // given instruction offset variable (even though there may + // be other offsets). + InstructionOffsetValue instructionOffsetValue = variables.oload(variableIndex); + branchUnit.branch(clazz, + codeAttribute, + offset, + instructionOffsetValue.instructionOffset(instructionOffsetValue.instructionOffsetCount()-1)); + break; + + default: + throw new IllegalArgumentException("Unknown variable instruction ["+variableInstruction.opcode+"]"); + } + } + + + 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(clazz, codeAttribute, offset, branchTarget, + stack.ipop().equal(valueFactory.createIntegerValue(0))); + break; + + case InstructionConstants.OP_IFNE: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.ipop().notEqual(valueFactory.createIntegerValue(0))); + break; + + case InstructionConstants.OP_IFLT: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.ipop().lessThan(valueFactory.createIntegerValue(0))); + break; + + case InstructionConstants.OP_IFGE: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.ipop().greaterThanOrEqual(valueFactory.createIntegerValue(0))); + break; + + case InstructionConstants.OP_IFGT: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.ipop().greaterThan(valueFactory.createIntegerValue(0))); + break; + + case InstructionConstants.OP_IFLE: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.ipop().lessThanOrEqual(valueFactory.createIntegerValue(0))); + break; + + + case InstructionConstants.OP_IFICMPEQ: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.ipop().equal(stack.ipop())); + break; + + case InstructionConstants.OP_IFICMPNE: + 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(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(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(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(clazz, codeAttribute, offset, branchTarget, + stack.ipop().greaterThanOrEqual(stack.ipop())); + break; + + case InstructionConstants.OP_IFACMPEQ: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.apop().equal(stack.apop())); + break; + + case InstructionConstants.OP_IFACMPNE: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.apop().notEqual(stack.apop())); + break; + + case InstructionConstants.OP_GOTO: + case InstructionConstants.OP_GOTO_W: + branchUnit.branch(clazz, codeAttribute, offset, branchTarget); + break; + + + case InstructionConstants.OP_JSR: + case InstructionConstants.OP_JSR_W: + stack.push(new InstructionOffsetValue(offset + + branchInstruction.length(offset))); + branchUnit.branch(clazz, codeAttribute, offset, branchTarget); + break; + + case InstructionConstants.OP_IFNULL: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.apop().isNull()); + break; + + case InstructionConstants.OP_IFNONNULL: + branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget, + stack.apop().isNotNull()); + break; + + default: + throw new IllegalArgumentException("Unknown branch instruction ["+branchInstruction.opcode+"]"); + } + } + + + 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(clazz, codeAttribute, + offset, + offset + tableSwitchInstruction.defaultOffset); + + for (int index = 0; index < tableSwitchInstruction.jumpOffsets.length; index++) + { + int conditional = indexValue.equal(valueFactory.createIntegerValue( + tableSwitchInstruction.lowCase + index)); + branchUnit.branchConditionally(clazz, codeAttribute, + offset, + offset + tableSwitchInstruction.jumpOffsets[index], + conditional); + + // If this branch is always taken, we can skip the rest. + if (conditional == Value.ALWAYS) + { + break; + } + } + } + + + 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(clazz, codeAttribute, + offset, + offset + lookUpSwitchInstruction.defaultOffset); + + for (int index = 0; index < lookUpSwitchInstruction.jumpOffsets.length; index++) + { + int conditional = indexValue.equal(valueFactory.createIntegerValue( + lookUpSwitchInstruction.cases[index])); + branchUnit.branchConditionally(clazz, codeAttribute, + offset, + offset + lookUpSwitchInstruction.jumpOffsets[index], + conditional); + + // If this branch is always taken, we can skip the rest. + if (conditional == Value.ALWAYS) + { + break; + } + } + } +} diff --git a/src/proguard/evaluation/Stack.java b/src/proguard/evaluation/Stack.java new file mode 100644 index 000000000..c449e86c4 --- /dev/null +++ b/src/proguard/evaluation/Stack.java @@ -0,0 +1,560 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +import java.util.Arrays; + +/** + * This class represents an operand stack that contains Value + * objects. + * + * @author Eric Lafortune + */ +public class Stack +{ + private static final TopValue TOP_VALUE = new TopValue(); + + + protected Value[] values; + protected int currentSize; + protected int actualMaxSize; + + + /** + * Creates a new Stack with a given maximum size, accounting for the double + * space required by Category 2 values. + */ + public Stack(int maxSize) + { + values = new Value[maxSize]; + } + + + /** + * Creates a Stack that is a copy of the given Stack. + */ + public Stack(Stack stack) + { + // Create the values array. + this(stack.values.length); + + // Copy the stack contents. + copy(stack); + } + + + /** + * Returns the actual maximum stack size that was required for all stack + * operations, accounting for the double space required by Category 2 values. + */ + public int getActualMaxSize() + { + return actualMaxSize; + } + + + /** + * Resets this Stack, so that it can be reused. + */ + public void reset(int maxSize) + { + // Is the values array large enough? + if (maxSize > values.length) + { + // Create a new one. + values = new Value[maxSize]; + } + + // Clear the sizes. + clear(); + + actualMaxSize = 0; + } + + + /** + * Copies the values of the given Stack into this Stack. + */ + public void copy(Stack other) + { + // Is the values array large enough? + if (other.values.length > values.length) + { + // Create a new one. + values = new Value[other.values.length]; + } + + // Copy the stack contents. + System.arraycopy(other.values, 0, this.values, 0, other.currentSize); + + // Copy the sizes. + currentSize = other.currentSize; + actualMaxSize = other.actualMaxSize; + } + + + /** + * Generalizes the values of this Stack with the values of the given Stack. + * The stacks must have the same current sizes. + * @return whether the generalization has made any difference. + */ + public boolean generalize(Stack other) + { + if (this.currentSize != other.currentSize) + { + throw new IllegalArgumentException("Stacks have different current sizes ["+this.currentSize+"] and ["+other.currentSize+"]"); + } + + boolean changed = false; + + // Generalize the stack values. + for (int index = 0; index < currentSize; index++) + { + Value thisValue = this.values[index]; + + if (thisValue != null) + { + Value newValue = null; + + Value otherValue = other.values[index]; + + if (otherValue != null) + { + newValue = thisValue.generalize(otherValue); + } + + changed = changed || !thisValue.equals(newValue); + + values[index] = newValue; + } + } + + // Check if the other stack extends beyond this one. + if (this.actualMaxSize < other.actualMaxSize) + { + this.actualMaxSize = other.actualMaxSize; + } + + return changed; + } + + + /** + * Clears the stack. + */ + public void clear() + { + // Clear the stack contents. + Arrays.fill(values, 0, currentSize, null); + + currentSize = 0; + } + + + /** + * Returns the number of elements currently on the stack, accounting for the + * double space required by Category 2 values. + */ + public int size() + { + return currentSize; + } + + + /** + * Gets the specified Value from the stack, without disturbing it. + * @param index the index of the stack element, counting from the bottom + * of the stack. + * @return the value at the specified position. + */ + public Value getBottom(int index) + { + return values[index]; + } + + + /** + * Sets the specified Value on the stack, without disturbing it. + * @param index the index of the stack element, counting from the bottom + * of the stack. + * @param value the value to set. + */ + public void setBottom(int index, Value value) + { + values[index] = value; + } + + + /** + * Gets the specified Value from the stack, without disturbing it. + * @param index the index of the stack element, counting from the top + * of the stack. + * @return the value at the specified position. + */ + public Value getTop(int index) + { + return values[currentSize - index - 1]; + } + + + /** + * Sets the specified Value on the stack, without disturbing it. + * @param index the index of the stack element, counting from the top + * of the stack. + * @param value the value to set. + */ + public void setTop(int index, Value value) + { + values[currentSize - index - 1] = value; + } + + + /** + * 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) + { + // Account for the extra space required by Category 2 values. + if (value.isCategory2()) + { + values[currentSize++] = TOP_VALUE; + } + + // Push the value. + values[currentSize++] = value; + + // Update the maximum actual size; + if (actualMaxSize < currentSize) + { + actualMaxSize = currentSize; + } + } + + + /** + * Pops the top Value from the stack. + */ + public Value pop() + { + Value value = values[--currentSize]; + + values[currentSize] = null; + + // Account for the extra space required by Category 2 values. + if (value.isCategory2()) + { + values[--currentSize] = null; + } + + return value; + } + + + // Pop methods that provide convenient casts to the expected value types. + + /** + * Pops the top IntegerValue from the stack. + */ + public IntegerValue ipop() + { + return pop().integerValue(); + } + + + /** + * Pops the top LongValue from the stack. + */ + public LongValue lpop() + { + return pop().longValue(); + } + + + /** + * Pops the top FloatValue from the stack. + */ + public FloatValue fpop() + { + return pop().floatValue(); + } + + + /** + * Pops the top DoubleValue from the stack. + */ + public DoubleValue dpop() + { + return pop().doubleValue(); + } + + + /** + * Pops the top ReferenceValue from the stack. + */ + public ReferenceValue apop() + { + return pop().referenceValue(); + } + + + /** + * Pops the top InstructionOffsetValue from the stack. + */ + public InstructionOffsetValue opop() + { + return pop().instructionOffsetValue(); + } + + + /** + * Pops the top category 1 value from the stack. + */ + public void pop1() + { + values[--currentSize] = null; + } + + + /** + * Pops the top category 2 value from the stack (or alternatively, two + * Category 1 stack elements). + */ + public void pop2() + { + values[--currentSize] = null; + values[--currentSize] = null; + } + + + /** + * Duplicates the top Category 1 value. + */ + public void dup() + { + values[currentSize] = values[currentSize - 1].category1Value(); + + currentSize++; + + // Update the maximum actual size; + if (actualMaxSize < currentSize) + { + actualMaxSize = currentSize; + } + } + + + /** + * Duplicates the top Category 1 value, one Category 1 element down the + * stack. + */ + public void dup_x1() + { + values[currentSize] = values[currentSize - 1].category1Value(); + values[currentSize - 1] = values[currentSize - 2].category1Value(); + values[currentSize - 2] = values[currentSize ]; + + currentSize++; + + // Update the maximum actual size; + if (actualMaxSize < currentSize) + { + actualMaxSize = currentSize; + } + } + + + /** + * Duplicates the top Category 1 value, two Category 1 elements (or one + * Category 2 element) down the stack. + */ + public void dup_x2() + { + values[currentSize] = values[currentSize - 1].category1Value(); + values[currentSize - 1] = values[currentSize - 2]; + values[currentSize - 2] = values[currentSize - 3]; + values[currentSize - 3] = values[currentSize ]; + + currentSize++; + + // Update the maximum actual size; + if (actualMaxSize < currentSize) + { + actualMaxSize = currentSize; + } + } + + /** + * Duplicates the top Category 2 value (or alternatively, the equivalent + * Category 1 stack elements). + */ + public void dup2() + { + values[currentSize ] = values[currentSize - 2]; + values[currentSize + 1] = values[currentSize - 1]; + + currentSize += 2; + + // Update the maximum actual size; + if (actualMaxSize < currentSize) + { + actualMaxSize = currentSize; + } + } + + + /** + * Duplicates the top Category 2 value, one Category 1 element down the + * stack (or alternatively, the equivalent Category 1 stack values). + */ + public void dup2_x1() + { + values[currentSize + 1] = values[currentSize - 1]; + values[currentSize ] = values[currentSize - 2]; + values[currentSize - 1] = values[currentSize - 3]; + values[currentSize - 2] = values[currentSize + 1]; + values[currentSize - 3] = values[currentSize ]; + + currentSize += 2; + + // Update the maximum actual size; + if (actualMaxSize < currentSize) + { + actualMaxSize = currentSize; + } + } + + + /** + * Duplicates the top Category 2 value, one Category 2 stack element down + * the stack (or alternatively, the equivalent Category 1 stack values). + */ + public void dup2_x2() + { + values[currentSize + 1] = values[currentSize - 1]; + values[currentSize ] = values[currentSize - 2]; + values[currentSize - 1] = values[currentSize - 3]; + values[currentSize - 2] = values[currentSize - 4]; + values[currentSize - 3] = values[currentSize + 1]; + values[currentSize - 4] = values[currentSize ]; + + currentSize += 2; + + // Update the maximum actual size; + if (actualMaxSize < currentSize) + { + actualMaxSize = currentSize; + } + } + + + /** + * Swaps the top two Category 1 values. + */ + public void swap() + { + Value value1 = values[currentSize - 1].category1Value(); + Value value2 = values[currentSize - 2].category1Value(); + + values[currentSize - 1] = value2; + values[currentSize - 2] = value1; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (object == null || + this.getClass() != object.getClass()) + { + return false; + } + + Stack other = (Stack)object; + + if (this.currentSize != other.currentSize) + { + return false; + } + + for (int index = 0; index < currentSize; index++) + { + Value thisValue = this.values[index]; + Value otherValue = other.values[index]; + if (thisValue == null ? otherValue != null : + !thisValue.equals(otherValue)) + { + return false; + } + } + + return true; + } + + + public int hashCode() + { + int hashCode = currentSize; + + for (int index = 0; index < currentSize; index++) + { + Value value = values[index]; + if (value != null) + { + hashCode ^= value.hashCode(); + } + } + + return hashCode; + } + + + public String toString() + { + StringBuffer buffer = new StringBuffer(); + + for (int index = 0; index < currentSize; index++) + { + Value value = values[index]; + buffer = buffer.append('[') + .append(value == null ? "empty" : value.toString()) + .append(']'); + } + + return buffer.toString(); + } +} diff --git a/src/proguard/evaluation/TracedStack.java b/src/proguard/evaluation/TracedStack.java new file mode 100644 index 000000000..08778a114 --- /dev/null +++ b/src/proguard/evaluation/TracedStack.java @@ -0,0 +1,342 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 Stack saves additional information with stack elements, to keep track + * of their origins and destinations. + *

+ * The stack stores a given producer Value along with each Value it stores. + * It then generalizes a given collected Value with the producer Value + * of each Value it loads. The producer Value and the initial collected Value + * can be set; the generalized collected Value can be retrieved. + * + * @author Eric Lafortune + */ +public class TracedStack extends Stack +{ + private Value producerValue; + private Stack producerStack; + + + /** + * Creates a new TracedStack with a given maximum size. + */ + public TracedStack(int maxSize) + { + super(maxSize); + + producerStack = new Stack(maxSize); + } + + + /** + * Creates a new TracedStack that is a copy of the given TracedStack. + */ + public TracedStack(TracedStack tracedStack) + { + super(tracedStack); + + producerStack = new Stack(tracedStack.producerStack); + } + + + /** + * Sets the Value that will be stored along with all push and pop + * instructions. + */ + public void setProducerValue(Value producerValue) + { + this.producerValue = producerValue; + } + + + /** + * Gets the specified producer Value from the stack, without disturbing it. + * @param index the index of the stack element, counting from the bottom + * of the stack. + * @return the producer value at the specified position. + */ + public Value getBottomProducerValue(int index) + { + return producerStack.getBottom(index); + } + + + /** + * Sets the specified producer Value on the stack, without disturbing it. + * @param index the index of the stack element, counting from the bottom + * of the stack. + * @param value the producer value to set. + */ + public void setBottomProducerValue(int index, Value value) + { + producerStack.setBottom(index, value); + } + + + /** + * Gets the specified producer Value from the stack, without disturbing it. + * @param index the index of the stack element, counting from the top + * of the stack. + * @return the producer value at the specified position. + */ + public Value getTopProducerValue(int index) + { + return producerStack.getTop(index); + } + + + /** + * Sets the specified producer Value on the stack, without disturbing it. + * @param index the index of the stack element, counting from the top + * of the stack. + * @param value the producer value to set. + */ + public void setTopProducerValue(int index, Value value) + { + producerStack.setTop(index, value); + } + + + // Implementations for Stack. + + public void reset(int size) + { + super.reset(size); + + producerStack.reset(size); + } + + public void copy(TracedStack other) + { + super.copy(other); + + producerStack.copy(other.producerStack); + } + + public boolean generalize(TracedStack other) + { + return + super.generalize(other) | + producerStack.generalize(other.producerStack); + } + + public void clear() + { + super.clear(); + + producerStack.clear(); + } + + public void removeTop(int index) + { + super.removeTop(index); + + producerStack.removeTop(index); + } + + public void push(Value value) + { + super.push(value); + + producerPush(); + + // Account for the extra space required by Category 2 values. + if (value.isCategory2()) + { + producerPush(); + } + } + + public Value pop() + { + Value value = super.pop(); + + producerPop(); + + // Account for the extra space required by Category 2 values. + if (value.isCategory2()) + { + producerPop(); + } + + return value; + } + + public void pop1() + { + super.pop1(); + + producerPop(); + } + + public void pop2() + { + super.pop2(); + + producerPop(); + producerPop(); + } + + public void dup() + { + super.dup(); + + producerPop(); + producerPush(); + producerPush(); + } + + public void dup_x1() + { + super.dup_x1(); + + producerPop(); + producerPop(); + producerPush(); + producerPush(); + producerPush(); + } + + public void dup_x2() + { + super.dup_x2(); + + producerPop(); + producerPop(); + producerPop(); + producerPush(); + producerPush(); + producerPush(); + producerPush(); + } + + public void dup2() + { + super.dup2(); + + producerPop(); + producerPop(); + producerPush(); + producerPush(); + producerPush(); + producerPush(); + } + + public void dup2_x1() + { + super.dup2_x1(); + + producerPop(); + producerPop(); + producerPop(); + producerPush(); + producerPush(); + producerPush(); + producerPush(); + producerPush(); + } + + public void dup2_x2() + { + super.dup2_x2(); + + producerPop(); + producerPop(); + producerPop(); + producerPop(); + producerPush(); + producerPush(); + producerPush(); + producerPush(); + producerPush(); + producerPush(); + } + + public void swap() + { + super.swap(); + + producerPop(); + producerPop(); + producerPush(); + producerPush(); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (object == null || + this.getClass() != object.getClass()) + { + return false; + } + + TracedStack other = (TracedStack)object; + + return super.equals(object) && + this.producerStack.equals(other.producerStack); + } + + + public int hashCode() + { + return super.hashCode() ^ + producerStack.hashCode(); + } + + + public String toString() + { + StringBuffer buffer = new StringBuffer(); + + for (int index = 0; index < this.size(); index++) + { + Value value = this.values[index]; + Value producerValue = producerStack.getBottom(index); + buffer = buffer.append('[') + .append(producerValue == null ? "empty:" : producerValue.toString()) + .append(value == null ? "empty" : value.toString()) + .append(']'); + } + + return buffer.toString(); + } + + + // Small utility methods. + + private void producerPush() + { + producerStack.push(producerValue); + } + + + private void producerPop() + { + producerStack.pop(); + } +} diff --git a/src/proguard/evaluation/TracedVariables.java b/src/proguard/evaluation/TracedVariables.java new file mode 100644 index 000000000..fef54e151 --- /dev/null +++ b/src/proguard/evaluation/TracedVariables.java @@ -0,0 +1,199 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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. + *

+ * 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. + * + * @author Eric Lafortune + */ +public class TracedVariables extends Variables +{ + public static final int NONE = -1; + + + private Value producerValue; + private Variables producerVariables; + + + /** + * Creates a new TracedVariables with a given size. + */ + public TracedVariables(int size) + { + super(size); + + producerVariables = new Variables(size); + } + + + /** + * Creates a new TracedVariables that is a copy of the given TracedVariables. + */ + public TracedVariables(TracedVariables tracedVariables) + { + super(tracedVariables); + + producerVariables = new Variables(tracedVariables.producerVariables); + } + + + /** + * Sets the Value that will be stored along with all store instructions. + */ + public void setProducerValue(Value producerValue) + { + this.producerValue = producerValue; + } + + + /** + * 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); + } + + + // 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, + 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; + + if (clearConflictingOtherVariables) + { + other.producerVariables.values[index] = null; + } + } + } + } + + return variablesChanged || producersChanged; + } + + + public void store(int index, Value value) + { + // Store the value itself in the variable. + super.store(index, value); + + // Store the producer value in its producer variable. + producerVariables.store(index, producerValue); + + // Account for the extra space required by Category 2 values. + if (value.isCategory2()) + { + producerVariables.store(index+1, producerValue); + } + } + + + // 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); + } + + + 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 producerValue = producerVariables.getValue(index); + buffer = buffer.append('[') + .append(producerValue == null ? "empty:" : producerValue.toString()) + .append(value == null ? "empty" : value.toString()) + .append(']'); + } + + return buffer.toString(); + } +} diff --git a/src/proguard/evaluation/Variables.java b/src/proguard/evaluation/Variables.java new file mode 100644 index 000000000..16b39e7cd --- /dev/null +++ b/src/proguard/evaluation/Variables.java @@ -0,0 +1,347 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +import java.util.Arrays; + +/** + * This class represents a local variable frame that contains Value + * objects. Values are generalizations of all values that have been stored in + * the respective variables. + * + * @author Eric Lafortune + */ +public class Variables +{ + private static final TopValue TOP_VALUE = new TopValue(); + + + protected Value[] values; + protected int size; + + + /** + * Creates a new Variables object with a given maximum number of variables. + */ + public Variables(int size) + { + this.values = new Value[size]; + this.size = size; + } + + + /** + * Creates a Variables object that is a copy of the given Variables object. + */ + public Variables(Variables variables) + { + // Create the values array. + this(variables.size); + + // Copy the values. + initialize(variables); + } + + + /** + * Resets this Variables object, so that it can be reused. + */ + public void reset(int size) + { + // Is the values array large enough? + if (size > values.length) + { + // Create a new one. + values = new Value[size]; + } + else + { + // Clear the variables. + Arrays.fill(values, null); + } + + this.size = size; + } + + + /** + * Initializes the values of this Variables object with the values of the + * given Variables object. The other object may have fewer values, in which + * case the remaining values are left unchanged. + */ + public void initialize(Variables other) + { + if (this.size < other.size) + { + throw new IllegalArgumentException("Variable frame is too small ["+this.size+"] compared to other frame ["+other.size+"]"); + } + + // Copy the values. + System.arraycopy(other.values, 0, this.values, 0, other.size); + } + + + /** + * Generalizes the values of this Variables object with the values of the + * 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, + boolean clearConflictingOtherVariables) + { + if (this.size != other.size) + { + throw new IllegalArgumentException("Variable frames have different sizes ["+this.size+"] and ["+other.size+"]"); + } + + boolean changed = false; + + for (int index = 0; index < size; index++) + { + Value thisValue = this.values[index]; + Value otherValue = other.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. Don't generalize the variable then, + // but let it clear instead. + if (thisValue != null && + otherValue != null && + thisValue.computationalType() == otherValue.computationalType()) + { + Value newValue = thisValue.generalize(otherValue); + + changed = changed || !thisValue.equals(newValue); + + this.values[index] = newValue; + } + else + { + changed = changed || thisValue != null; + + this.values[index] = null; + + if (clearConflictingOtherVariables) + { + other.values[index] = null; + } + } + } + + return changed; + } + + + /** + * Returns the number of variables. + */ + public int size() + { + return size; + } + + + /** + * 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) + { + if (index < 0 || + index >= size) + { + throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]"); + } + + // Store the value. + values[index] = value; + + // Account for the extra space required by Category 2 values. + if (value.isCategory2()) + { + values[index + 1] = TOP_VALUE; + } + } + + + /** + * Loads the Value from the variable with the given index. + */ + public Value load(int index) + { + if (index < 0 || + index >= size) + { + throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]"); + } + + return values[index]; + } + + + // Load methods that provide convenient casts to the expected value types. + + /** + * Loads the IntegerValue from the variable with the given index. + */ + public IntegerValue iload(int index) + { + return load(index).integerValue(); + } + + + /** + * Loads the LongValue from the variable with the given index. + */ + public LongValue lload(int index) + { + return load(index).longValue(); + } + + + /** + * Loads the FloatValue from the variable with the given index. + */ + public FloatValue fload(int index) + { + return load(index).floatValue(); + } + + + /** + * Loads the DoubleValue from the variable with the given index. + */ + public DoubleValue dload(int index) + { + return load(index).doubleValue(); + } + + + /** + * Loads the ReferenceValue from the variable with the given index. + */ + public ReferenceValue aload(int index) + { + return load(index).referenceValue(); + } + + + /** + * Loads the InstructionOffsetValue from the variable with the given index. + */ + public InstructionOffsetValue oload(int index) + { + return load(index).instructionOffsetValue(); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (object == null || + this.getClass() != object.getClass()) + { + return false; + } + + Variables other = (Variables)object; + + if (this.size != other.size) + { + return false; + } + + for (int index = 0; index < size; index++) + { + Value thisValue = this.values[index]; + Value otherValue = other.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. + // We'll ignore these. + if (thisValue != null && + otherValue != null && + thisValue.computationalType() == otherValue.computationalType() && + !thisValue.equals(otherValue)) + { + return false; + } + } + + return true; + } + + + public int hashCode() + { + int hashCode = size; + + for (int index = 0; index < size; index++) + { + Value value = values[index]; + if (value != null) + { + hashCode ^= value.hashCode(); + } + } + + return hashCode; + } + + + public String toString() + { + StringBuffer buffer = new StringBuffer(); + + for (int index = 0; index < size; index++) + { + Value value = values[index]; + buffer = buffer.append('[') + .append(value == null ? "empty" : value.toString()) + .append(']'); + } + + return buffer.toString(); + } +} diff --git a/src/proguard/evaluation/value/Category1Value.java b/src/proguard/evaluation/value/Category1Value.java new file mode 100644 index 000000000..a5ebf8673 --- /dev/null +++ b/src/proguard/evaluation/value/Category1Value.java @@ -0,0 +1,41 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 abstract class represents a partially evaluated Category 1 value. + * + * @author Eric Lafortune + */ +public abstract class Category1Value extends Value +{ + // Implementations for Value. + + public final Category1Value category1Value() + { + return this; + } + + public final boolean isCategory2() + { + return false; + } +} diff --git a/src/proguard/evaluation/value/Category2Value.java b/src/proguard/evaluation/value/Category2Value.java new file mode 100644 index 000000000..2be6e719a --- /dev/null +++ b/src/proguard/evaluation/value/Category2Value.java @@ -0,0 +1,41 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 abstract class represents a partially evaluated Category 2 value. + * + * @author Eric Lafortune + */ +public abstract class Category2Value extends Value +{ + // Implementations for Value. + + public final Category2Value category2Value() + { + return this; + } + + public final boolean isCategory2() + { + return true; + } +} diff --git a/src/proguard/evaluation/value/ComparisonValue.java b/src/proguard/evaluation/value/ComparisonValue.java new file mode 100644 index 000000000..abbf31c4b --- /dev/null +++ b/src/proguard/evaluation/value/ComparisonValue.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents the result of a comparisons of two scalar + * values. + * + * @author Eric Lafortune + */ +final class ComparisonValue extends SpecificIntegerValue +{ + private final Value value1; + private final Value value2; + + + /** + * Creates a new comparison integer value of the two given scalar values. + */ + public ComparisonValue(Value value1, + Value value2) + { + this.value1 = value1; + this.value2 = value2; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.value1.equals(((ComparisonValue)object).value1) && + this.value2.equals(((ComparisonValue)object).value2); + } + + + public int hashCode() + { + return super.hashCode() ^ + value1.hashCode() ^ + value2.hashCode(); + } + + + public String toString() + { + return "("+value1+"~"+ value2 +")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/CompositeDoubleValue.java b/src/proguard/evaluation/value/CompositeDoubleValue.java new file mode 100644 index 000000000..be739ed52 --- /dev/null +++ b/src/proguard/evaluation/value/CompositeDoubleValue.java @@ -0,0 +1,81 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 DoubleValue represents the result of a binary operation on two double + * values. + * + * @author Eric Lafortune + */ +final class CompositeDoubleValue extends SpecificDoubleValue +{ + public static final byte ADD = '+'; + public static final byte SUBTRACT = '-'; + public static final byte MULTIPLY = '*'; + public static final byte DIVIDE = '/'; + public static final byte REMAINDER = '%'; + + + private final DoubleValue doubleValue1; + private final byte operation; + private final DoubleValue doubleValue2; + + + /** + * Creates a new composite double value of the two given double values + * and the given operation. + */ + public CompositeDoubleValue(DoubleValue doubleValue1, + byte operation, + DoubleValue doubleValue2) + { + this.doubleValue1 = doubleValue1; + this.operation = operation; + this.doubleValue2 = doubleValue2; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.doubleValue1.equals(((CompositeDoubleValue)object).doubleValue1) && + this.operation == ((CompositeDoubleValue)object).operation && + this.doubleValue2.equals(((CompositeDoubleValue)object).doubleValue2); + } + + + public int hashCode() + { + return super.hashCode() ^ + doubleValue1.hashCode() ^ + doubleValue2.hashCode(); + } + + + public String toString() + { + return "("+doubleValue1+((char)operation)+doubleValue2+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/CompositeFloatValue.java b/src/proguard/evaluation/value/CompositeFloatValue.java new file mode 100644 index 000000000..096106837 --- /dev/null +++ b/src/proguard/evaluation/value/CompositeFloatValue.java @@ -0,0 +1,81 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 FloatValue represents the result of a binary operation on two float + * values. + * + * @author Eric Lafortune + */ +final class CompositeFloatValue extends SpecificFloatValue +{ + public static final byte ADD = '+'; + public static final byte SUBTRACT = '-'; + public static final byte MULTIPLY = '*'; + public static final byte DIVIDE = '/'; + public static final byte REMAINDER = '%'; + + + private final FloatValue floatValue1; + private final byte operation; + private final FloatValue floatValue2; + + + /** + * Creates a new composite float value of the two given float values + * and the given operation. + */ + public CompositeFloatValue(FloatValue floatValue1, + byte operation, + FloatValue floatValue2) + { + this.floatValue1 = floatValue1; + this.operation = operation; + this.floatValue2 = floatValue2; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.floatValue1.equals(((CompositeFloatValue)object).floatValue1) && + this.operation == ((CompositeFloatValue)object).operation && + this.floatValue2.equals(((CompositeFloatValue)object).floatValue2); + } + + + public int hashCode() + { + return super.hashCode() ^ + floatValue1.hashCode() ^ + floatValue2.hashCode(); + } + + + public String toString() + { + return "("+floatValue1+((char)operation)+floatValue2+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/CompositeIntegerValue.java b/src/proguard/evaluation/value/CompositeIntegerValue.java new file mode 100644 index 000000000..97caa2f6b --- /dev/null +++ b/src/proguard/evaluation/value/CompositeIntegerValue.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents the result of a binary operation on two integer + * values. + * + * @author Eric Lafortune + */ +final class CompositeIntegerValue extends SpecificIntegerValue +{ + public static final byte ADD = '+'; + public static final byte SUBTRACT = '-'; + public static final byte MULTIPLY = '*'; + public static final byte DIVIDE = '/'; + public static final byte REMAINDER = '%'; + public static final byte SHIFT_LEFT = '<'; + public static final byte SHIFT_RIGHT = '>'; + public static final byte UNSIGNED_SHIFT_RIGHT = '}'; + public static final byte AND = '&'; + public static final byte OR = '|'; + public static final byte XOR = '^'; + + + private final IntegerValue integerValue1; + private final byte operation; + private final IntegerValue integerValue2; + + + /** + * Creates a new composite integer value of the two given integer values + * and the given operation. + */ + public CompositeIntegerValue(IntegerValue integerValue1, + byte operation, + IntegerValue integerValue2) + { + this.integerValue1 = integerValue1; + this.operation = operation; + this.integerValue2 = integerValue2; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.integerValue1.equals(((CompositeIntegerValue)object).integerValue1) && + this.operation == ((CompositeIntegerValue)object).operation && + this.integerValue2.equals(((CompositeIntegerValue)object).integerValue2); + } + + + public int hashCode() + { + return super.hashCode() ^ + integerValue1.hashCode() ^ + integerValue2.hashCode(); + } + + + public String toString() + { + return "("+integerValue1+((char)operation)+integerValue2+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/CompositeLongValue.java b/src/proguard/evaluation/value/CompositeLongValue.java new file mode 100644 index 000000000..3b8a97f71 --- /dev/null +++ b/src/proguard/evaluation/value/CompositeLongValue.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 LongValue represents the result of a binary operation on two long + * values. + * + * @author Eric Lafortune + */ +final class CompositeLongValue extends SpecificLongValue +{ + public static final byte ADD = '+'; + public static final byte SUBTRACT = '-'; + public static final byte MULTIPLY = '*'; + public static final byte DIVIDE = '/'; + public static final byte REMAINDER = '%'; + public static final byte SHIFT_LEFT = '<'; + public static final byte SHIFT_RIGHT = '>'; + public static final byte UNSIGNED_SHIFT_RIGHT = '}'; + public static final byte AND = '&'; + public static final byte OR = '|'; + public static final byte XOR = '^'; + + + private final LongValue longValue1; + private final byte operation; + private final Value longValue2; + + + /** + * Creates a new composite long value of the two given long values + * and the given operation. + */ + public CompositeLongValue(LongValue longValue1, + byte operation, + Value longValue2) + { + this.longValue1 = longValue1; + this.operation = operation; + this.longValue2 = longValue2; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.longValue1.equals(((CompositeLongValue)object).longValue1) && + this.operation == ((CompositeLongValue)object).operation && + this.longValue2.equals(((CompositeLongValue)object).longValue2); + } + + + public int hashCode() + { + return super.hashCode() ^ + longValue1.hashCode() ^ + longValue2.hashCode(); + } + + + public String toString() + { + return "("+longValue1+((char)operation)+longValue2+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ConvertedByteValue.java b/src/proguard/evaluation/value/ConvertedByteValue.java new file mode 100644 index 000000000..eb1a35049 --- /dev/null +++ b/src/proguard/evaluation/value/ConvertedByteValue.java @@ -0,0 +1,64 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents a byte value that is converted from an + * integer value. + * + * @author Eric Lafortune + */ +final class ConvertedByteValue extends SpecificIntegerValue +{ + private final IntegerValue value; + + + /** + * Creates a new converted byte value of the given integer value. + */ + public ConvertedByteValue(IntegerValue value) + { + this.value = value; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.value.equals(((ConvertedByteValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + value.hashCode(); + } + + + public String toString() + { + return "(byte)("+value+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ConvertedCharacterValue.java b/src/proguard/evaluation/value/ConvertedCharacterValue.java new file mode 100644 index 000000000..a491bedcb --- /dev/null +++ b/src/proguard/evaluation/value/ConvertedCharacterValue.java @@ -0,0 +1,64 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents a character value that is converted from an + * integer value. + * + * @author Eric Lafortune + */ +final class ConvertedCharacterValue extends SpecificIntegerValue +{ + private final IntegerValue value; + + + /** + * Creates a new converted character value of the given integer value. + */ + public ConvertedCharacterValue(IntegerValue value) + { + this.value = value; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.value.equals(((ConvertedCharacterValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + value.hashCode(); + } + + + public String toString() + { + return "(char)("+value+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ConvertedDoubleValue.java b/src/proguard/evaluation/value/ConvertedDoubleValue.java new file mode 100644 index 000000000..65fab84f3 --- /dev/null +++ b/src/proguard/evaluation/value/ConvertedDoubleValue.java @@ -0,0 +1,64 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 DoubleValue represents a double value that is converted from another + * scalar value. + * + * @author Eric Lafortune + */ +final class ConvertedDoubleValue extends SpecificDoubleValue +{ + private final Value value; + + + /** + * Creates a new converted double value of the given value. + */ + public ConvertedDoubleValue(Value value) + { + this.value = value; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.value.equals(((ConvertedDoubleValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + value.hashCode(); + } + + + public String toString() + { + return "(double)("+value+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ConvertedFloatValue.java b/src/proguard/evaluation/value/ConvertedFloatValue.java new file mode 100644 index 000000000..e74ec8d5e --- /dev/null +++ b/src/proguard/evaluation/value/ConvertedFloatValue.java @@ -0,0 +1,64 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 FloatValue represents a float value that is converted from another + * scalar value. + * + * @author Eric Lafortune + */ +final class ConvertedFloatValue extends SpecificFloatValue +{ + private final Value value; + + + /** + * Creates a new converted float value of the given value. + */ + public ConvertedFloatValue(Value value) + { + this.value = value; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.value.equals(((ConvertedFloatValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + value.hashCode(); + } + + + public String toString() + { + return "(float)("+value+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ConvertedIntegerValue.java b/src/proguard/evaluation/value/ConvertedIntegerValue.java new file mode 100644 index 000000000..273b5a16a --- /dev/null +++ b/src/proguard/evaluation/value/ConvertedIntegerValue.java @@ -0,0 +1,64 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents a integer value that is converted from another + * scalar value. + * + * @author Eric Lafortune + */ +final class ConvertedIntegerValue extends SpecificIntegerValue +{ + private final Value value; + + + /** + * Creates a new converted integer value of the given value. + */ + public ConvertedIntegerValue(Value value) + { + this.value = value; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.value.equals(((ConvertedIntegerValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + value.hashCode(); + } + + + public String toString() + { + return "(int)("+value+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ConvertedLongValue.java b/src/proguard/evaluation/value/ConvertedLongValue.java new file mode 100644 index 000000000..5cb9104ac --- /dev/null +++ b/src/proguard/evaluation/value/ConvertedLongValue.java @@ -0,0 +1,64 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 LongValue represents a long value that is converted from another + * scalar value. + * + * @author Eric Lafortune + */ +final class ConvertedLongValue extends SpecificLongValue +{ + private final Value value; + + + /** + * Creates a new converted long value of the given value. + */ + public ConvertedLongValue(Value value) + { + this.value = value; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.value.equals(((ConvertedLongValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + value.hashCode(); + } + + + public String toString() + { + return "(long)("+value+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ConvertedShortValue.java b/src/proguard/evaluation/value/ConvertedShortValue.java new file mode 100644 index 000000000..cef2a206c --- /dev/null +++ b/src/proguard/evaluation/value/ConvertedShortValue.java @@ -0,0 +1,64 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents a short value that is converted from an + * integer value. + * + * @author Eric Lafortune + */ +final class ConvertedShortValue extends SpecificIntegerValue +{ + private final IntegerValue value; + + + /** + * Creates a new converted short value of the given integer value. + */ + public ConvertedShortValue(IntegerValue value) + { + this.value = value; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.value.equals(((ConvertedShortValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + value.hashCode(); + } + + + public String toString() + { + return "(short)("+value+")"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/DoubleValue.java b/src/proguard/evaluation/value/DoubleValue.java new file mode 100644 index 000000000..6630b2ff8 --- /dev/null +++ b/src/proguard/evaluation/value/DoubleValue.java @@ -0,0 +1,359 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +/** + * This class represents a partially evaluated double value. + * + * @author Eric Lafortune + */ +public abstract class DoubleValue extends Category2Value +{ + /** + * Returns the specific double value, if applicable. + */ + public double value() + { + return 0.0; + } + + + // Basic unary methods. + + /** + * Returns the negated value of this DoubleValue. + */ + public abstract DoubleValue negate(); + + /** + * Converts this DoubleValue to an IntegerValue. + */ + public abstract IntegerValue convertToInteger(); + + /** + * Converts this DoubleValue to a LongValue. + */ + public abstract LongValue convertToLong(); + + /** + * Converts this DoubleValue to a FloatValue. + */ + public abstract FloatValue convertToFloat(); + + + // Basic binary methods. + + /** + * Returns the generalization of this DoubleValue and the given other + * DoubleValue. + */ + public abstract DoubleValue generalize(DoubleValue other); + + + /** + * Returns the sum of this DoubleValue and the given DoubleValue. + */ + public abstract DoubleValue add(DoubleValue other); + + /** + * Returns the difference of this DoubleValue and the given DoubleValue. + */ + public abstract DoubleValue subtract(DoubleValue other); + + /** + * Returns the difference of the given DoubleValue and this DoubleValue. + */ + public abstract DoubleValue subtractFrom(DoubleValue other); + + /** + * Returns the product of this DoubleValue and the given DoubleValue. + */ + public abstract DoubleValue multiply(DoubleValue other); + + /** + * Returns the quotient of this DoubleValue and the given DoubleValue. + */ + public abstract DoubleValue divide(DoubleValue other); + + /** + * Returns the quotient of the given DoubleValue and this DoubleValue. + */ + public abstract DoubleValue divideOf(DoubleValue other); + + /** + * Returns the remainder of this DoubleValue divided by the given DoubleValue. + */ + public abstract DoubleValue remainder(DoubleValue other); + + /** + * Returns the remainder of the given DoubleValue divided by this DoubleValue. + */ + public abstract DoubleValue remainderOf(DoubleValue other); + + /** + * 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 abstract IntegerValue compare(DoubleValue other); + + + // Derived binary methods. + + /** + * 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) + { + return compare(other).negate(); + } + + + // Similar binary methods, but this time with more specific arguments. + + /** + * Returns the generalization of this DoubleValue and the given other + * SpecificDoubleValue. + */ + public DoubleValue generalize(SpecificDoubleValue other) + { + return generalize((DoubleValue)other); + } + + + /** + * Returns the sum of this DoubleValue and the given SpecificDoubleValue. + */ + public DoubleValue add(SpecificDoubleValue other) + { + return add((DoubleValue)other); + } + + /** + * Returns the difference of this DoubleValue and the given SpecificDoubleValue. + */ + public DoubleValue subtract(SpecificDoubleValue other) + { + return subtract((DoubleValue)other); + } + + /** + * Returns the difference of the given SpecificDoubleValue and this DoubleValue. + */ + public DoubleValue subtractFrom(SpecificDoubleValue other) + { + return subtractFrom((DoubleValue)other); + } + + /** + * Returns the product of this DoubleValue and the given SpecificDoubleValue. + */ + public DoubleValue multiply(SpecificDoubleValue other) + { + return multiply((DoubleValue)other); + } + + /** + * Returns the quotient of this DoubleValue and the given SpecificDoubleValue. + */ + public DoubleValue divide(SpecificDoubleValue other) + { + return divide((DoubleValue)other); + } + + /** + * Returns the quotient of the given SpecificDoubleValue and this + * DoubleValue. + */ + public DoubleValue divideOf(SpecificDoubleValue other) + { + return divideOf((DoubleValue)other); + } + + /** + * Returns the remainder of this DoubleValue divided by the given + * SpecificDoubleValue. + */ + public DoubleValue remainder(SpecificDoubleValue other) + { + return remainder((DoubleValue)other); + } + + /** + * Returns the remainder of the given SpecificDoubleValue and this + * DoubleValue. + */ + public DoubleValue remainderOf(SpecificDoubleValue other) + { + return remainderOf((DoubleValue)other); + } + + /** + * 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) + { + return compare((DoubleValue)other); + } + + + // Derived binary methods. + + /** + * 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 final IntegerValue compareReverse(SpecificDoubleValue other) + { + return compare(other).negate(); + } + + + // Similar binary methods, but this time with particular arguments. + + /** + * Returns the generalization of this DoubleValue and the given other + * ParticularDoubleValue. + */ + public DoubleValue generalize(ParticularDoubleValue other) + { + return generalize((SpecificDoubleValue)other); + } + + + /** + * Returns the sum of this DoubleValue and the given ParticularDoubleValue. + */ + public DoubleValue add(ParticularDoubleValue other) + { + return add((SpecificDoubleValue)other); + } + + /** + * Returns the difference of this DoubleValue and the given ParticularDoubleValue. + */ + public DoubleValue subtract(ParticularDoubleValue other) + { + return subtract((SpecificDoubleValue)other); + } + + /** + * Returns the difference of the given ParticularDoubleValue and this DoubleValue. + */ + public DoubleValue subtractFrom(ParticularDoubleValue other) + { + return subtractFrom((SpecificDoubleValue)other); + } + + /** + * Returns the product of this DoubleValue and the given ParticularDoubleValue. + */ + public DoubleValue multiply(ParticularDoubleValue other) + { + return multiply((SpecificDoubleValue)other); + } + + /** + * Returns the quotient of this DoubleValue and the given ParticularDoubleValue. + */ + public DoubleValue divide(ParticularDoubleValue other) + { + return divide((SpecificDoubleValue)other); + } + + /** + * Returns the quotient of the given ParticularDoubleValue and this + * DoubleValue. + */ + public DoubleValue divideOf(ParticularDoubleValue other) + { + return divideOf((SpecificDoubleValue)other); + } + + /** + * Returns the remainder of this DoubleValue divided by the given + * ParticularDoubleValue. + */ + public DoubleValue remainder(ParticularDoubleValue other) + { + return remainder((SpecificDoubleValue)other); + } + + /** + * Returns the remainder of the given ParticularDoubleValue and this + * DoubleValue. + */ + public DoubleValue remainderOf(ParticularDoubleValue other) + { + return remainderOf((SpecificDoubleValue)other); + } + + /** + * Returns an IntegerValue with value -1, 0, or 1, if this DoubleValue is + * less than, equal to, or greater than the given ParticularDoubleValue, + * respectively. + */ + public IntegerValue compare(ParticularDoubleValue other) + { + return compare((SpecificDoubleValue)other); + } + + + // Derived binary methods. + + /** + * Returns an IntegerValue with value 1, 0, or -1, if this DoubleValue is + * less than, equal to, or greater than the given ParticularDoubleValue, + * respectively. + */ + public final IntegerValue compareReverse(ParticularDoubleValue other) + { + return compare(other).negate(); + } + + + // Implementations for Value. + + public final DoubleValue doubleValue() + { + return this; + } + + public final Value generalize(Value other) + { + return this.generalize(other.doubleValue()); + } + + public final int computationalType() + { + return TYPE_DOUBLE; + } + + public final String internalType() + { + return String.valueOf(ClassConstants.INTERNAL_TYPE_DOUBLE); + } +} diff --git a/src/proguard/evaluation/value/FloatValue.java b/src/proguard/evaluation/value/FloatValue.java new file mode 100644 index 000000000..6dc8bee86 --- /dev/null +++ b/src/proguard/evaluation/value/FloatValue.java @@ -0,0 +1,359 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +/** + * This class represents a partially evaluated float value. + * + * @author Eric Lafortune + */ +public abstract class FloatValue extends Category1Value +{ + /** + * Returns the specific float value, if applicable. + */ + public float value() + { + return 0f; + } + + + // Basic unary methods. + + /** + * Returns the negated value of this FloatValue. + */ + public abstract FloatValue negate(); + + /** + * Converts this FloatValue to an IntegerValue. + */ + public abstract IntegerValue convertToInteger(); + + /** + * Converts this FloatValue to a LongValue. + */ + public abstract LongValue convertToLong(); + + /** + * Converts this FloatValue to a DoubleValue. + */ + public abstract DoubleValue convertToDouble(); + + + // Basic binary methods. + + /** + * Returns the generalization of this FloatValue and the given other + * FloatValue. + */ + public abstract FloatValue generalize(FloatValue other); + + + /** + * Returns the sum of this FloatValue and the given FloatValue. + */ + public abstract FloatValue add(FloatValue other); + + /** + * Returns the difference of this FloatValue and the given FloatValue. + */ + public abstract FloatValue subtract(FloatValue other); + + /** + * Returns the difference of the given FloatValue and this FloatValue. + */ + public abstract FloatValue subtractFrom(FloatValue other); + + /** + * Returns the product of this FloatValue and the given FloatValue. + */ + public abstract FloatValue multiply(FloatValue other); + + /** + * Returns the quotient of this FloatValue and the given FloatValue. + */ + public abstract FloatValue divide(FloatValue other); + + /** + * Returns the quotient of the given FloatValue and this FloatValue. + */ + public abstract FloatValue divideOf(FloatValue other); + + /** + * Returns the remainder of this FloatValue divided by the given FloatValue. + */ + public abstract FloatValue remainder(FloatValue other); + + /** + * Returns the remainder of the given FloatValue divided by this FloatValue. + */ + public abstract FloatValue remainderOf(FloatValue other); + + /** + * 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 abstract IntegerValue compare(FloatValue other); + + + // Derived binary methods. + + /** + * 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) + { + return compare(other).negate(); + } + + + // Similar binary methods, but this time with more specific arguments. + + /** + * Returns the generalization of this FloatValue and the given other + * SpecificFloatValue. + */ + public FloatValue generalize(SpecificFloatValue other) + { + return generalize((FloatValue)other); + } + + + /** + * Returns the sum of this FloatValue and the given SpecificFloatValue. + */ + public FloatValue add(SpecificFloatValue other) + { + return add((FloatValue)other); + } + + /** + * Returns the difference of this FloatValue and the given SpecificFloatValue. + */ + public FloatValue subtract(SpecificFloatValue other) + { + return subtract((FloatValue)other); + } + + /** + * Returns the difference of the given SpecificFloatValue and this FloatValue. + */ + public FloatValue subtractFrom(SpecificFloatValue other) + { + return subtractFrom((FloatValue)other); + } + + /** + * Returns the product of this FloatValue and the given SpecificFloatValue. + */ + public FloatValue multiply(SpecificFloatValue other) + { + return multiply((FloatValue)other); + } + + /** + * Returns the quotient of this FloatValue and the given SpecificFloatValue. + */ + public FloatValue divide(SpecificFloatValue other) + { + return divide((FloatValue)other); + } + + /** + * Returns the quotient of the given SpecificFloatValue and this + * FloatValue. + */ + public FloatValue divideOf(SpecificFloatValue other) + { + return divideOf((FloatValue)other); + } + + /** + * Returns the remainder of this FloatValue divided by the given + * SpecificFloatValue. + */ + public FloatValue remainder(SpecificFloatValue other) + { + return remainder((FloatValue)other); + } + + /** + * Returns the remainder of the given SpecificFloatValue and this + * FloatValue. + */ + public FloatValue remainderOf(SpecificFloatValue other) + { + return remainderOf((FloatValue)other); + } + + /** + * 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) + { + return compare((FloatValue)other); + } + + + // Derived binary methods. + + /** + * 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 final IntegerValue compareReverse(SpecificFloatValue other) + { + return compare(other).negate(); + } + + + // Similar binary methods, but this time with particular arguments. + + /** + * Returns the generalization of this FloatValue and the given other + * ParticularFloatValue. + */ + public FloatValue generalize(ParticularFloatValue other) + { + return generalize((SpecificFloatValue)other); + } + + + /** + * Returns the sum of this FloatValue and the given ParticularFloatValue. + */ + public FloatValue add(ParticularFloatValue other) + { + return add((SpecificFloatValue)other); + } + + /** + * Returns the difference of this FloatValue and the given ParticularFloatValue. + */ + public FloatValue subtract(ParticularFloatValue other) + { + return subtract((SpecificFloatValue)other); + } + + /** + * Returns the difference of the given ParticularFloatValue and this FloatValue. + */ + public FloatValue subtractFrom(ParticularFloatValue other) + { + return subtractFrom((SpecificFloatValue)other); + } + + /** + * Returns the product of this FloatValue and the given ParticularFloatValue. + */ + public FloatValue multiply(ParticularFloatValue other) + { + return multiply((SpecificFloatValue)other); + } + + /** + * Returns the quotient of this FloatValue and the given ParticularFloatValue. + */ + public FloatValue divide(ParticularFloatValue other) + { + return divide((SpecificFloatValue)other); + } + + /** + * Returns the quotient of the given ParticularFloatValue and this + * FloatValue. + */ + public FloatValue divideOf(ParticularFloatValue other) + { + return divideOf((SpecificFloatValue)other); + } + + /** + * Returns the remainder of this FloatValue divided by the given + * ParticularFloatValue. + */ + public FloatValue remainder(ParticularFloatValue other) + { + return remainder((SpecificFloatValue)other); + } + + /** + * Returns the remainder of the given ParticularFloatValue and this + * FloatValue. + */ + public FloatValue remainderOf(ParticularFloatValue other) + { + return remainderOf((SpecificFloatValue)other); + } + + /** + * Returns an IntegerValue with value -1, 0, or 1, if this FloatValue is + * less than, equal to, or greater than the given ParticularFloatValue, + * respectively. + */ + public IntegerValue compare(ParticularFloatValue other) + { + return compare((SpecificFloatValue)other); + } + + + // Derived binary methods. + + /** + * Returns an IntegerValue with value 1, 0, or -1, if this FloatValue is + * less than, equal to, or greater than the given ParticularFloatValue, + * respectively. + */ + public final IntegerValue compareReverse(ParticularFloatValue other) + { + return compare(other).negate(); + } + + + // Implementations for Value. + + public final FloatValue floatValue() + { + return this; + } + + public final Value generalize(Value other) + { + return this.generalize(other.floatValue()); + } + + public final int computationalType() + { + return TYPE_FLOAT; + } + + public final String internalType() + { + return String.valueOf(ClassConstants.INTERNAL_TYPE_FLOAT); + } +} diff --git a/src/proguard/evaluation/value/IdentifiedDoubleValue.java b/src/proguard/evaluation/value/IdentifiedDoubleValue.java new file mode 100644 index 000000000..4ff24668f --- /dev/null +++ b/src/proguard/evaluation/value/IdentifiedDoubleValue.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 DoubleValue represents a double value that is identified by a unique ID. + * + * @author Eric Lafortune + */ +final class IdentifiedDoubleValue extends SpecificDoubleValue +{ + private final ValueFactory valuefactory; + private final int id; + + + /** + * Creates a new double value with the given ID. + */ + public IdentifiedDoubleValue(ValueFactory valuefactory, int id) + { + this.valuefactory = valuefactory; + this.id = id; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.valuefactory.equals(((IdentifiedDoubleValue)object).valuefactory) && + this.id == ((IdentifiedDoubleValue)object).id; + } + + + public int hashCode() + { + return super.hashCode() ^ + valuefactory.hashCode() ^ + id; + } + + + public String toString() + { + return "d"+id; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/IdentifiedFloatValue.java b/src/proguard/evaluation/value/IdentifiedFloatValue.java new file mode 100644 index 000000000..c8349bcbb --- /dev/null +++ b/src/proguard/evaluation/value/IdentifiedFloatValue.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 FloatValue represents a float value that is identified by a unique ID. + * + * @author Eric Lafortune + */ +final class IdentifiedFloatValue extends SpecificFloatValue +{ + private final ValueFactory valuefactory; + private final int id; + + + /** + * Creates a new float value with the given ID. + */ + public IdentifiedFloatValue(ValueFactory valuefactory, int id) + { + this.valuefactory = valuefactory; + this.id = id; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.valuefactory.equals(((IdentifiedFloatValue)object).valuefactory) && + this.id == ((IdentifiedFloatValue)object).id; + } + + + public int hashCode() + { + return super.hashCode() ^ + valuefactory.hashCode() ^ + id; + } + + + public String toString() + { + return "f"+id; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/IdentifiedIntegerValue.java b/src/proguard/evaluation/value/IdentifiedIntegerValue.java new file mode 100644 index 000000000..6c3ee5d8e --- /dev/null +++ b/src/proguard/evaluation/value/IdentifiedIntegerValue.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents a integer value that is identified by a unique ID. + * + * @author Eric Lafortune + */ +final class IdentifiedIntegerValue extends SpecificIntegerValue +{ + private final ValueFactory valuefactory; + private final int id; + + + /** + * Creates a new integer value with the given ID. + */ + public IdentifiedIntegerValue(ValueFactory valuefactory, int id) + { + this.valuefactory = valuefactory; + this.id = id; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.valuefactory.equals(((IdentifiedIntegerValue)object).valuefactory) && + this.id == ((IdentifiedIntegerValue)object).id; + } + + + public int hashCode() + { + return super.hashCode() ^ + valuefactory.hashCode() ^ + id; + } + + + public String toString() + { + return "i"+id; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/IdentifiedLongValue.java b/src/proguard/evaluation/value/IdentifiedLongValue.java new file mode 100644 index 000000000..e0b68f2de --- /dev/null +++ b/src/proguard/evaluation/value/IdentifiedLongValue.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 LongValue represents a long value that is identified by a unique ID. + * + * @author Eric Lafortune + */ +final class IdentifiedLongValue extends SpecificLongValue +{ + private final ValueFactory valuefactory; + private final int id; + + + /** + * Creates a new long value with the given ID. + */ + public IdentifiedLongValue(ValueFactory valuefactory, int id) + { + this.valuefactory = valuefactory; + this.id = id; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.valuefactory.equals(((IdentifiedLongValue)object).valuefactory) && + this.id == ((IdentifiedLongValue)object).id; + } + + + public int hashCode() + { + return super.hashCode() ^ + valuefactory.hashCode() ^ + id; + } + + + public String toString() + { + return "l"+id; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/IdentifiedReferenceValue.java b/src/proguard/evaluation/value/IdentifiedReferenceValue.java new file mode 100644 index 000000000..5cfbd6093 --- /dev/null +++ b/src/proguard/evaluation/value/IdentifiedReferenceValue.java @@ -0,0 +1,102 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.Clazz; + +/** + * This LongValue represents a reference value that is identified by a unique ID. + * + * @author Eric Lafortune + */ +final class IdentifiedReferenceValue extends ReferenceValue +{ + private final ValueFactory valuefactory; + private final int id; + + + /** + * Creates a new long value with the given ID. + */ + public IdentifiedReferenceValue(String type, + Clazz referencedClass, + boolean mayBeNull, + ValueFactory valuefactory, + int id) + { + super(type, referencedClass, mayBeNull); + + this.valuefactory = valuefactory; + this.id = id; + } + + + // Implementations for ReferenceValue. + + public int equal(ReferenceValue other) + { + return this.equals(other) ? ALWAYS : MAYBE; + } + + + // Implementations of binary methods of ReferenceValue. + + public ReferenceValue generalize(ReferenceValue other) + { + // Remove the ID if both values don't share the same ID. + return this.equals(other) ? + this : + new ReferenceValue(type, referencedClass, mayBeNull).generalize(other); + } + + + // Implementations for Value. + + public boolean isSpecific() + { + return true; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.valuefactory.equals(((IdentifiedReferenceValue)object).valuefactory) && + this.id == ((IdentifiedReferenceValue)object).id; + } + + + public int hashCode() + { + return super.hashCode() ^ + valuefactory.hashCode() ^ + id; + } + + + public String toString() + { + return super.toString()+'#'+id; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/IdentifiedValueFactory.java b/src/proguard/evaluation/value/IdentifiedValueFactory.java new file mode 100644 index 000000000..be5c88570 --- /dev/null +++ b/src/proguard/evaluation/value/IdentifiedValueFactory.java @@ -0,0 +1,75 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue objects. + * + * @author Eric Lafortune + */ +public class IdentifiedValueFactory +extends SpecificValueFactory +{ + private int integerID; + private int longID; + private int floatID; + private int doubleID; + private int referenceID; + + + // Implementations for ValueFactory. + + public IntegerValue createIntegerValue() + { + return new IdentifiedIntegerValue(this, integerID++); + } + + + public LongValue createLongValue() + { + return new IdentifiedLongValue(this, longID++); + } + + + public FloatValue createFloatValue() + { + return new IdentifiedFloatValue(this, floatID++); + } + + + public DoubleValue createDoubleValue() + { + return new IdentifiedDoubleValue(this, doubleID++); + } + + + public ReferenceValue createReferenceValue(String type, + Clazz referencedClass, + boolean mayBeNull) + { + return type == null ? + REFERENCE_VALUE_NULL : + new IdentifiedReferenceValue(type, referencedClass, mayBeNull, this, referenceID++); + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/InstructionOffsetValue.java b/src/proguard/evaluation/value/InstructionOffsetValue.java new file mode 100644 index 000000000..07a44ee36 --- /dev/null +++ b/src/proguard/evaluation/value/InstructionOffsetValue.java @@ -0,0 +1,307 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +/** + * This class represents a partially evaluated instruction offset. It can + * contain 0 or more specific instruction offsets. + * + * @author Eric Lafortune + */ +public class InstructionOffsetValue extends Category1Value +{ + public static final InstructionOffsetValue EMPTY_VALUE = new InstructionOffsetValue(); + + + private int[] values; + + + private InstructionOffsetValue() + { + } + + + public InstructionOffsetValue(int value) + { + this.values = new int[] { value }; + } + + + public InstructionOffsetValue(int[] values) + { + this.values = values; + } + + + public int instructionOffsetCount() + { + return values == null ? 0 : values.length; + } + + + public int instructionOffset(int index) + { + return values[index]; + } + + + /** + * Returns whether the given value is present in this list of instruction + * offsets. + */ + public boolean contains(int value) + { + if (values != null) + { + for (int index = 0; index < values.length; index++) + { + if (values[index] == value) + { + return true; + } + } + } + + return false; + } + + + /** + * Returns the minimum value from this list of instruction offsets. + * Returns Integer.MAX_VALUE 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 Integer.MIN_VALUE 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. + */ + public final Value generalize(InstructionOffsetValue other) + { + // If the values array of either is null, return the other one. + if (this.values == null) + { + return other; + } + + if (other.values == null) + { + return this; + } + + // Compute the length of the union of the arrays. + int newLength = this.values.length; + for (int index = 0; index < other.values.length; index++) + { + if (!this.contains(other.values[index])) + { + newLength++; + } + } + + // If the length of the union array is equal to the length of the values + // array of either, return it. + if (newLength == other.values.length) + { + return other; + } + + // The ordering of the this array may not be right, so we can't just + // use it. + //if (newLength == this.values.length) + //{ + // return this; + //} + + // Create the union array. + int[] newValues = new int[newLength]; + + int newIndex = 0; + + // Copy the values that are different from the other array. + for (int index = 0; index < this.values.length; index++) + { + if (!other.contains(this.values[index])) + { + newValues[newIndex++] = this.values[index]; + } + } + + // Copy the values from the other array. + for (int index = 0; index < other.values.length; index++) + { + newValues[newIndex++] = other.values[index]; + } + + return new InstructionOffsetValue(newValues); + } + + + // Implementations for Value. + + public final InstructionOffsetValue instructionOffsetValue() + { + return this; + } + + public boolean isSpecific() + { + return true; + } + + public boolean isParticular() + { + return true; + } + + public final Value generalize(Value other) + { + return this.generalize(other.instructionOffsetValue()); + } + + public final int computationalType() + { + return TYPE_INSTRUCTION_OFFSET; + } + + public final String internalType() + { + return String.valueOf(ClassConstants.INTERNAL_TYPE_INT); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (object == null || + this.getClass() != object.getClass()) + { + return false; + } + + InstructionOffsetValue other = (InstructionOffsetValue)object; + if (this.values == other.values) + { + return true; + } + + if (this.values == null || + other.values == null || + this.values.length != other.values.length) + { + return false; + } + + for (int index = 0; index < other.values.length; index++) + { + if (!this.contains(other.values[index])) + { + return false; + } + } + + return true; + } + + + public int hashCode() + { + int hashCode = this.getClass().hashCode(); + + if (values != null) + { + for (int index = 0; index < values.length; index++) + { + hashCode ^= values[index]; + } + } + + return hashCode; + } + + + public String toString() + { + StringBuffer buffer = new StringBuffer(); + + if (values != null) + { + for (int index = 0; index < values.length; index++) + { + if (index > 0) + { + buffer.append(','); + } + buffer.append(values[index]); + } + } + + return buffer.append(':').toString(); + } +} diff --git a/src/proguard/evaluation/value/IntegerValue.java b/src/proguard/evaluation/value/IntegerValue.java new file mode 100644 index 000000000..b1824c67b --- /dev/null +++ b/src/proguard/evaluation/value/IntegerValue.java @@ -0,0 +1,1002 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +/** + * This class represents a partially evaluated integer value. + * + * @author Eric Lafortune + */ +public abstract class IntegerValue extends Category1Value +{ + /** + * Returns the specific integer value, if applicable. + */ + public int value() + { + return 0; + } + + + // Basic unary methods. + + /** + * Returns the negated value of this IntegerValue. + */ + public abstract IntegerValue negate(); + + /** + * Converts this IntegerValue to a byte IntegerValue. + */ + public abstract IntegerValue convertToByte(); + + /** + * Converts this IntegerValue to a character IntegerValue. + */ + public abstract IntegerValue convertToCharacter(); + + /** + * Converts this IntegerValue to a short IntegerValue. + */ + public abstract IntegerValue convertToShort(); + + /** + * Converts this IntegerValue to a LongValue. + */ + public abstract LongValue convertToLong(); + + /** + * Converts this IntegerValue to a FloatValue. + */ + public abstract FloatValue convertToFloat(); + + /** + * Converts this IntegerValue to a DoubleValue. + */ + public abstract DoubleValue convertToDouble(); + + + // Basic binary methods. + + /** + * Returns the generalization of this IntegerValue and the given other + * IntegerValue. + */ + public abstract IntegerValue generalize(IntegerValue other); + + /** + * Returns the sum of this IntegerValue and the given IntegerValue. + */ + public abstract IntegerValue add(IntegerValue other); + + /** + * Returns the difference of this IntegerValue and the given IntegerValue. + */ + public abstract IntegerValue subtract(IntegerValue other); + + /** + * Returns the difference of the given IntegerValue and this IntegerValue. + */ + public abstract IntegerValue subtractFrom(IntegerValue other); + + /** + * Returns the product of this IntegerValue and the given IntegerValue. + */ + public abstract IntegerValue multiply(IntegerValue other) + throws ArithmeticException; + + /** + * Returns the quotient of this IntegerValue and the given IntegerValue. + */ + public abstract IntegerValue divide(IntegerValue other) + throws ArithmeticException; + + /** + * Returns the quotient of the given IntegerValue and this IntegerValue. + */ + public abstract IntegerValue divideOf(IntegerValue other) + throws ArithmeticException; + + /** + * Returns the remainder of this IntegerValue divided by the given + * IntegerValue. + */ + public abstract IntegerValue remainder(IntegerValue other) + throws ArithmeticException; + + /** + * Returns the remainder of the given IntegerValue divided by this + * IntegerValue. + */ + public abstract IntegerValue remainderOf(IntegerValue other) + throws ArithmeticException; + + /** + * Returns this IntegerValue, shifted left by the given IntegerValue. + */ + public abstract IntegerValue shiftLeft(IntegerValue other); + + /** + * Returns this IntegerValue, shifted right by the given IntegerValue. + */ + public abstract IntegerValue shiftRight(IntegerValue other); + + /** + * Returns this unsigned IntegerValue, shifted left by the given + * IntegerValue. + */ + public abstract IntegerValue unsignedShiftRight(IntegerValue other); + + /** + * Returns the given IntegerValue, shifted left by this IntegerValue. + */ + public abstract IntegerValue shiftLeftOf(IntegerValue other); + + /** + * Returns the given IntegerValue, shifted right by this IntegerValue. + */ + public abstract IntegerValue shiftRightOf(IntegerValue other); + + /** + * Returns the given unsigned IntegerValue, shifted left by this + * IntegerValue. + */ + public abstract IntegerValue unsignedShiftRightOf(IntegerValue other); + + /** + * Returns the given LongValue, shifted left by this IntegerValue. + */ + public abstract LongValue shiftLeftOf(LongValue other); + + /** + * Returns the given LongValue, shifted right by this IntegerValue. + */ + public abstract LongValue shiftRightOf(LongValue other); + + /** + * Returns the given unsigned LongValue, shifted right by this IntegerValue. + */ + public abstract LongValue unsignedShiftRightOf(LongValue other); + + /** + * Returns the logical and of this IntegerValue and the given + * IntegerValue. + */ + public abstract IntegerValue and(IntegerValue other); + + /** + * Returns the logical or of this IntegerValue and the given + * IntegerValue. + */ + public abstract IntegerValue or(IntegerValue other); + + /** + * Returns the logical xor of this IntegerValue and the given + * IntegerValue. + */ + public abstract IntegerValue xor(IntegerValue other); + + /** + * Returns whether this IntegerValue and the given IntegerValue are equal: + * NEVER, MAYBE, or ALWAYS. + */ + public abstract int equal(IntegerValue other); + + /** + * Returns whether this IntegerValue is less than the given IntegerValue: + * NEVER, MAYBE, or ALWAYS. + */ + public abstract int lessThan(IntegerValue other); + + /** + * Returns whether this IntegerValue is less than or equal to the given + * IntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public abstract int lessThanOrEqual(IntegerValue other); + + + // Derived binary methods. + + /** + * Returns whether this IntegerValue and the given IntegerValue are different: + * NEVER, MAYBE, or ALWAYS. + */ + public final int notEqual(IntegerValue other) + { + return -equal(other); + } + + /** + * Returns whether this IntegerValue is greater than the given IntegerValue: + * NEVER, MAYBE, or ALWAYS. + */ + public final int greaterThan(IntegerValue other) + { + return -lessThanOrEqual(other); + } + + /** + * Returns whether this IntegerValue is greater than or equal to the given IntegerValue: + * NEVER, MAYBE, or ALWAYS. + */ + public final int greaterThanOrEqual(IntegerValue other) + { + return -lessThan(other); + } + + + // Similar binary methods, but this time with unknown arguments. + + /** + * Returns the generalization of this IntegerValue and the given other + * UnknownIntegerValue. + */ + public IntegerValue generalize(UnknownIntegerValue other) + { + return generalize((IntegerValue)other); + } + + + /** + * Returns the sum of this IntegerValue and the given UnknownIntegerValue. + */ + public IntegerValue add(UnknownIntegerValue other) + { + return add((IntegerValue)other); + } + + /** + * Returns the difference of this IntegerValue and the given UnknownIntegerValue. + */ + public IntegerValue subtract(UnknownIntegerValue other) + { + return subtract((IntegerValue)other); + } + + /** + * Returns the difference of the given UnknownIntegerValue and this IntegerValue. + */ + public IntegerValue subtractFrom(UnknownIntegerValue other) + { + return subtractFrom((IntegerValue)other); + } + + /** + * Returns the product of this IntegerValue and the given UnknownIntegerValue. + */ + public IntegerValue multiply(UnknownIntegerValue other) + { + return multiply((IntegerValue)other); + } + + /** + * Returns the quotient of this IntegerValue and the given + * UnknownIntegerValue. + */ + public IntegerValue divide(UnknownIntegerValue other) + { + return divide((IntegerValue)other); + } + + /** + * Returns the quotient of the given UnknownIntegerValue and this + * IntegerValue. + */ + public IntegerValue divideOf(UnknownIntegerValue other) + { + return divideOf((IntegerValue)other); + } + + /** + * Returns the remainder of this IntegerValue divided by the given + * UnknownIntegerValue. + */ + public IntegerValue remainder(UnknownIntegerValue other) + { + return remainder((IntegerValue)other); + } + + /** + * Returns the remainder of the given UnknownIntegerValue divided by this + * IntegerValue. + */ + public IntegerValue remainderOf(UnknownIntegerValue other) + { + return remainderOf((IntegerValue)other); + } + + /** + * Returns this IntegerValue, shifted left by the given UnknownIntegerValue. + */ + public IntegerValue shiftLeft(UnknownIntegerValue other) + { + return shiftLeft((IntegerValue)other); + } + + /** + * Returns this IntegerValue, shifted right by the given UnknownIntegerValue. + */ + public IntegerValue shiftRight(UnknownIntegerValue other) + { + return shiftRight((IntegerValue)other); + } + + /** + * Returns this unsigned IntegerValue, shifted right by the given + * UnknownIntegerValue. + */ + public IntegerValue unsignedShiftRight(UnknownIntegerValue other) + { + return unsignedShiftRight((IntegerValue)other); + } + + /** + * Returns the given UnknownIntegerValue, shifted left by this IntegerValue. + */ + public IntegerValue shiftLeftOf(UnknownIntegerValue other) + { + return shiftLeftOf((IntegerValue)other); + } + + /** + * Returns the given UnknownIntegerValue, shifted right by this IntegerValue. + */ + public IntegerValue shiftRightOf(UnknownIntegerValue other) + { + return shiftRightOf((IntegerValue)other); + } + + /** + * Returns the given unsigned UnknownIntegerValue, shifted right by this + * IntegerValue. + */ + public IntegerValue unsignedShiftRightOf(UnknownIntegerValue other) + { + return unsignedShiftRightOf((IntegerValue)other); + } + + /** + * Returns the given UnknownLongValue, shifted left by this IntegerValue. + */ + public LongValue shiftLeftOf(UnknownLongValue other) + { + return shiftLeftOf((LongValue)other); + } + + /** + * Returns the given UnknownLongValue, shifted right by this IntegerValue. + */ + public LongValue shiftRightOf(UnknownLongValue other) + { + return shiftRightOf((LongValue)other); + } + + /** + * Returns the given unsigned UnknownLongValue, shifted right by this + * IntegerValue. + */ + public LongValue unsignedShiftRightOf(UnknownLongValue other) + { + return unsignedShiftRightOf((LongValue)other); + } + + /** + * Returns the logical and of this IntegerValue and the given + * UnknownIntegerValue. + */ + public IntegerValue and(UnknownIntegerValue other) + { + return and((IntegerValue)other); + } + + /** + * Returns the logical or of this IntegerValue and the given + * UnknownIntegerValue. + */ + public IntegerValue or(UnknownIntegerValue other) + { + return or((IntegerValue)other); + } + + /** + * Returns the logical xor of this IntegerValue and the given + * UnknownIntegerValue. + */ + public IntegerValue xor(UnknownIntegerValue other) + { + return xor((IntegerValue)other); + } + + /** + * Returns whether this IntegerValue and the given UnknownIntegerValue are + * equal: NEVER, MAYBE, or ALWAYS. + */ + public int equal(UnknownIntegerValue other) + { + return equal((IntegerValue)other); + } + + /** + * Returns whether this IntegerValue is less than the given + * UnknownIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public int lessThan(UnknownIntegerValue other) + { + return lessThan((IntegerValue)other); + } + + /** + * Returns whether this IntegerValue is less than or equal to the given + * UnknownIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public int lessThanOrEqual(UnknownIntegerValue other) + { + return lessThanOrEqual((IntegerValue)other); + } + + + // Derived binary methods. + + /** + * Returns whether this IntegerValue and the given UnknownIntegerValue are + * different: NEVER, MAYBE, or ALWAYS. + */ + public final int notEqual(UnknownIntegerValue other) + { + return -equal(other); + } + + /** + * Returns whether this IntegerValue is greater than the given + * UnknownIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public final int greaterThan(UnknownIntegerValue other) + { + return -lessThanOrEqual(other); + } + + /** + * Returns whether this IntegerValue is greater than or equal to the given + * UnknownIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public final int greaterThanOrEqual(UnknownIntegerValue other) + { + return -lessThan(other); + } + + + // Similar binary methods, but this time with specific arguments. + + /** + * Returns the generalization of this IntegerValue and the given other + * SpecificIntegerValue. + */ + public IntegerValue generalize(SpecificIntegerValue other) + { + return generalize((IntegerValue)other); + } + + + /** + * Returns the sum of this IntegerValue and the given SpecificIntegerValue. + */ + public IntegerValue add(SpecificIntegerValue other) + { + return add((IntegerValue)other); + } + + /** + * Returns the difference of this IntegerValue and the given SpecificIntegerValue. + */ + public IntegerValue subtract(SpecificIntegerValue other) + { + return subtract((IntegerValue)other); + } + + /** + * Returns the difference of the given SpecificIntegerValue and this IntegerValue. + */ + public IntegerValue subtractFrom(SpecificIntegerValue other) + { + return subtractFrom((IntegerValue)other); + } + + /** + * Returns the product of this IntegerValue and the given SpecificIntegerValue. + */ + public IntegerValue multiply(SpecificIntegerValue other) + { + return multiply((IntegerValue)other); + } + + /** + * Returns the quotient of this IntegerValue and the given + * SpecificIntegerValue. + */ + public IntegerValue divide(SpecificIntegerValue other) + { + return divide((IntegerValue)other); + } + + /** + * Returns the quotient of the given SpecificIntegerValue and this + * IntegerValue. + */ + public IntegerValue divideOf(SpecificIntegerValue other) + { + return divideOf((IntegerValue)other); + } + + /** + * Returns the remainder of this IntegerValue divided by the given + * SpecificIntegerValue. + */ + public IntegerValue remainder(SpecificIntegerValue other) + { + return remainder((IntegerValue)other); + } + + /** + * Returns the remainder of the given SpecificIntegerValue divided by this + * IntegerValue. + */ + public IntegerValue remainderOf(SpecificIntegerValue other) + { + return remainderOf((IntegerValue)other); + } + + /** + * Returns this IntegerValue, shifted left by the given SpecificIntegerValue. + */ + public IntegerValue shiftLeft(SpecificIntegerValue other) + { + return shiftLeft((IntegerValue)other); + } + + /** + * Returns this IntegerValue, shifted right by the given SpecificIntegerValue. + */ + public IntegerValue shiftRight(SpecificIntegerValue other) + { + return shiftRight((IntegerValue)other); + } + + /** + * Returns this unsigned IntegerValue, shifted right by the given + * SpecificIntegerValue. + */ + public IntegerValue unsignedShiftRight(SpecificIntegerValue other) + { + return unsignedShiftRight((IntegerValue)other); + } + + /** + * Returns the given SpecificIntegerValue, shifted left by this IntegerValue. + */ + public IntegerValue shiftLeftOf(SpecificIntegerValue other) + { + return shiftLeftOf((IntegerValue)other); + } + + /** + * Returns the given SpecificIntegerValue, shifted right by this IntegerValue. + */ + public IntegerValue shiftRightOf(SpecificIntegerValue other) + { + return shiftRightOf((IntegerValue)other); + } + + /** + * Returns the given unsigned SpecificIntegerValue, shifted right by this + * IntegerValue. + */ + public IntegerValue unsignedShiftRightOf(SpecificIntegerValue other) + { + return unsignedShiftRightOf((IntegerValue)other); + } + + /** + * Returns the given SpecificLongValue, shifted left by this IntegerValue. + */ + public LongValue shiftLeftOf(SpecificLongValue other) + { + return shiftLeftOf((LongValue)other); + } + + /** + * Returns the given SpecificLongValue, shifted right by this IntegerValue. + */ + public LongValue shiftRightOf(SpecificLongValue other) + { + return shiftRightOf((LongValue)other); + } + + /** + * Returns the given unsigned SpecificLongValue, shifted right by this + * IntegerValue. + */ + public LongValue unsignedShiftRightOf(SpecificLongValue other) + { + return unsignedShiftRightOf((LongValue)other); + } + + /** + * Returns the logical and of this IntegerValue and the given + * SpecificIntegerValue. + */ + public IntegerValue and(SpecificIntegerValue other) + { + return and((IntegerValue)other); + } + + /** + * Returns the logical or of this IntegerValue and the given + * SpecificIntegerValue. + */ + public IntegerValue or(SpecificIntegerValue other) + { + return or((IntegerValue)other); + } + + /** + * Returns the logical xor of this IntegerValue and the given + * SpecificIntegerValue. + */ + public IntegerValue xor(SpecificIntegerValue other) + { + return xor((IntegerValue)other); + } + + /** + * Returns whether this IntegerValue and the given SpecificIntegerValue are + * equal: NEVER, MAYBE, or ALWAYS. + */ + public int equal(SpecificIntegerValue other) + { + return equal((IntegerValue)other); + } + + /** + * Returns whether this IntegerValue is less than the given + * SpecificIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public int lessThan(SpecificIntegerValue other) + { + return lessThan((IntegerValue)other); + } + + /** + * Returns whether this IntegerValue is less than or equal to the given + * SpecificIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public int lessThanOrEqual(SpecificIntegerValue other) + { + return lessThanOrEqual((IntegerValue)other); + } + + + // Derived binary methods. + + /** + * Returns whether this IntegerValue and the given SpecificIntegerValue are + * different: NEVER, MAYBE, or ALWAYS. + */ + public final int notEqual(SpecificIntegerValue other) + { + return -equal(other); + } + + /** + * Returns whether this IntegerValue is greater than the given + * SpecificIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public final int greaterThan(SpecificIntegerValue other) + { + return -lessThanOrEqual(other); + } + + /** + * Returns whether this IntegerValue is greater than or equal to the given + * SpecificIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public final int greaterThanOrEqual(SpecificIntegerValue other) + { + return -lessThan(other); + } + + + // Similar binary methods, but this time with particular arguments. + + /** + * Returns the generalization of this IntegerValue and the given other + * ParticularIntegerValue. + */ + public IntegerValue generalize(ParticularIntegerValue other) + { + return generalize((SpecificIntegerValue)other); + } + + + /** + * Returns the sum of this IntegerValue and the given ParticularIntegerValue. + */ + public IntegerValue add(ParticularIntegerValue other) + { + return add((SpecificIntegerValue)other); + } + + /** + * Returns the difference of this IntegerValue and the given ParticularIntegerValue. + */ + public IntegerValue subtract(ParticularIntegerValue other) + { + return subtract((SpecificIntegerValue)other); + } + + /** + * Returns the difference of the given ParticularIntegerValue and this IntegerValue. + */ + public IntegerValue subtractFrom(ParticularIntegerValue other) + { + return subtractFrom((SpecificIntegerValue)other); + } + + /** + * Returns the product of this IntegerValue and the given ParticularIntegerValue. + */ + public IntegerValue multiply(ParticularIntegerValue other) + { + return multiply((SpecificIntegerValue)other); + } + + /** + * Returns the quotient of this IntegerValue and the given + * ParticularIntegerValue. + */ + public IntegerValue divide(ParticularIntegerValue other) + { + return divide((SpecificIntegerValue)other); + } + + /** + * Returns the quotient of the given ParticularIntegerValue and this + * IntegerValue. + */ + public IntegerValue divideOf(ParticularIntegerValue other) + { + return divideOf((SpecificIntegerValue)other); + } + + /** + * Returns the remainder of this IntegerValue divided by the given + * ParticularIntegerValue. + */ + public IntegerValue remainder(ParticularIntegerValue other) + { + return remainder((SpecificIntegerValue)other); + } + + /** + * Returns the remainder of the given ParticularIntegerValue divided by this + * IntegerValue. + */ + public IntegerValue remainderOf(ParticularIntegerValue other) + { + return remainderOf((SpecificIntegerValue)other); + } + + /** + * Returns this IntegerValue, shifted left by the given ParticularIntegerValue. + */ + public IntegerValue shiftLeft(ParticularIntegerValue other) + { + return shiftLeft((SpecificIntegerValue)other); + } + + /** + * Returns this IntegerValue, shifted right by the given ParticularIntegerValue. + */ + public IntegerValue shiftRight(ParticularIntegerValue other) + { + return shiftRight((SpecificIntegerValue)other); + } + + /** + * Returns this unsigned IntegerValue, shifted right by the given + * ParticularIntegerValue. + */ + public IntegerValue unsignedShiftRight(ParticularIntegerValue other) + { + return unsignedShiftRight((SpecificIntegerValue)other); + } + + /** + * Returns the given ParticularIntegerValue, shifted left by this IntegerValue. + */ + public IntegerValue shiftLeftOf(ParticularIntegerValue other) + { + return shiftLeftOf((SpecificIntegerValue)other); + } + + /** + * Returns the given ParticularIntegerValue, shifted right by this IntegerValue. + */ + public IntegerValue shiftRightOf(ParticularIntegerValue other) + { + return shiftRightOf((SpecificIntegerValue)other); + } + + /** + * Returns the given unsigned ParticularIntegerValue, shifted right by this + * IntegerValue. + */ + public IntegerValue unsignedShiftRightOf(ParticularIntegerValue other) + { + return unsignedShiftRightOf((SpecificIntegerValue)other); + } + + /** + * Returns the given ParticularLongValue, shifted left by this IntegerValue. + */ + public LongValue shiftLeftOf(ParticularLongValue other) + { + return shiftLeftOf((SpecificLongValue)other); + } + + /** + * Returns the given ParticularLongValue, shifted right by this IntegerValue. + */ + public LongValue shiftRightOf(ParticularLongValue other) + { + return shiftRightOf((SpecificLongValue)other); + } + + /** + * Returns the given unsigned ParticularLongValue, shifted right by this + * IntegerValue. + */ + public LongValue unsignedShiftRightOf(ParticularLongValue other) + { + return unsignedShiftRightOf((SpecificLongValue)other); + } + + /** + * Returns the logical and of this IntegerValue and the given + * ParticularIntegerValue. + */ + public IntegerValue and(ParticularIntegerValue other) + { + return and((SpecificIntegerValue)other); + } + + /** + * Returns the logical or of this IntegerValue and the given + * ParticularIntegerValue. + */ + public IntegerValue or(ParticularIntegerValue other) + { + return or((SpecificIntegerValue)other); + } + + /** + * Returns the logical xor of this IntegerValue and the given + * ParticularIntegerValue. + */ + public IntegerValue xor(ParticularIntegerValue other) + { + return xor((SpecificIntegerValue)other); + } + + /** + * Returns whether this IntegerValue and the given ParticularIntegerValue are + * equal: NEVER, MAYBE, or ALWAYS. + */ + public int equal(ParticularIntegerValue other) + { + return equal((SpecificIntegerValue)other); + } + + /** + * Returns whether this IntegerValue is less than the given + * ParticularIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public int lessThan(ParticularIntegerValue other) + { + return lessThan((SpecificIntegerValue)other); + } + + /** + * Returns whether this IntegerValue is less than or equal to the given + * ParticularIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public int lessThanOrEqual(ParticularIntegerValue other) + { + return lessThanOrEqual((SpecificIntegerValue)other); + } + + + // Derived binary methods. + + /** + * Returns whether this IntegerValue and the given ParticularIntegerValue are + * different: NEVER, MAYBE, or ALWAYS. + */ + public final int notEqual(ParticularIntegerValue other) + { + return -equal(other); + } + + /** + * Returns whether this IntegerValue is greater than the given + * ParticularIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public final int greaterThan(ParticularIntegerValue other) + { + return -lessThanOrEqual(other); + } + + /** + * Returns whether this IntegerValue is greater than or equal to the given + * ParticularIntegerValue: NEVER, MAYBE, or + * ALWAYS. + */ + public final int greaterThanOrEqual(ParticularIntegerValue other) + { + return -lessThan(other); + } + + + // Implementations for Value. + + public final IntegerValue integerValue() + { + return this; + } + + public final Value generalize(Value other) + { + return this.generalize(other.integerValue()); + } + + public final int computationalType() + { + return TYPE_INTEGER; + } + + public final String internalType() + { + return String.valueOf(ClassConstants.INTERNAL_TYPE_INT); + } +} diff --git a/src/proguard/evaluation/value/LongValue.java b/src/proguard/evaluation/value/LongValue.java new file mode 100644 index 000000000..e23c13c43 --- /dev/null +++ b/src/proguard/evaluation/value/LongValue.java @@ -0,0 +1,554 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +/** + * This class represents a partially evaluated long value. + * + * @author Eric Lafortune + */ +public abstract class LongValue extends Category2Value +{ + /** + * Returns the specific long value, if applicable. + */ + public long value() + { + return 0; + } + + + // Basic unary methods. + + /** + * Returns the negated value of this LongValue. + */ + public abstract LongValue negate(); + + + /** + * Converts this LongValue to an IntegerValue. + */ + public abstract IntegerValue convertToInteger(); + + /** + * Converts this LongValue to a FloatValue. + */ + public abstract FloatValue convertToFloat(); + + /** + * Converts this LongValue to a DoubleValue. + */ + public abstract DoubleValue convertToDouble(); + + + // Basic binary methods. + + /** + * Returns the generalization of this LongValue and the given other + * LongValue. + */ + public LongValue generalize(LongValue other) + { + return other.generalize(this); + } + + /** + * Returns the sum of this LongValue and the given LongValue. + */ + public LongValue add(LongValue other) + { + return other.add(this); + } + + /** + * Returns the difference of this LongValue and the given LongValue. + */ + public LongValue subtract(LongValue other) + { + return other.subtractFrom(this); + } + + /** + * Returns the difference of the given LongValue and this LongValue. + */ + public LongValue subtractFrom(LongValue other) + { + return other.subtract(this); + } + + /** + * Returns the product of this LongValue and the given LongValue. + */ + public LongValue multiply(LongValue other) + throws ArithmeticException + { + return other.multiply(this); + } + + /** + * Returns the quotient of this LongValue and the given LongValue. + */ + public LongValue divide(LongValue other) + throws ArithmeticException + { + return other.divideOf(this); + } + + /** + * Returns the quotient of the given LongValue and this LongValue. + */ + public LongValue divideOf(LongValue other) + throws ArithmeticException + { + return other.divide(this); + } + + /** + * Returns the remainder of this LongValue divided by the given + * LongValue. + */ + public LongValue remainder(LongValue other) + throws ArithmeticException + { + return other.remainderOf(this); + } + + /** + * Returns the remainder of the given LongValue divided by this + * LongValue. + */ + public LongValue remainderOf(LongValue other) + throws ArithmeticException + { + return other.remainder(this); + } + + /** + * Returns this LongValue, shifted left by the given IntegerValue. + */ + public LongValue shiftLeft(IntegerValue other) + { + return other.shiftLeftOf(this); + } + + /** + * Returns this LongValue, shifted right by the given IntegerValue. + */ + public LongValue shiftRight(IntegerValue other) + { + return other.shiftRightOf(this); + } + + /** + * Returns this unsigned LongValue, shifted left by the given + * IntegerValue. + */ + public LongValue unsignedShiftRight(IntegerValue other) + { + return other.unsignedShiftRightOf(this); + } + + /** + * Returns the logical and of this LongValue and the given + * LongValue. + */ + public LongValue and(LongValue other) + { + return other.and(this); + } + + /** + * Returns the logical or of this LongValue and the given + * LongValue. + */ + public LongValue or(LongValue other) + { + return other.or(this); + } + + /** + * Returns the logical xor of this LongValue and the given + * LongValue. + */ + public LongValue xor(LongValue other) + { + return other.xor(this); + } + + /** + * 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) + { + return other.compareReverse(this); + } + + + // Derived binary methods. + + /** + * 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) + { + return compare(other).negate(); + } + + + // Similar binary methods, but this time with more specific arguments. + + /** + * Returns the generalization of this LongValue and the given other + * SpecificLongValue. + */ + public LongValue generalize(SpecificLongValue other) + { + return this; + } + + + /** + * Returns the sum of this LongValue and the given SpecificLongValue. + */ + public LongValue add(SpecificLongValue other) + { + return this; + } + + /** + * Returns the difference of this LongValue and the given SpecificLongValue. + */ + public LongValue subtract(SpecificLongValue other) + { + return this; + } + + /** + * Returns the difference of the given SpecificLongValue and this LongValue. + */ + public LongValue subtractFrom(SpecificLongValue other) + { + return this; + } + + /** + * Returns the product of this LongValue and the given SpecificLongValue. + */ + public LongValue multiply(SpecificLongValue other) + { + return this; + } + + /** + * Returns the quotient of this LongValue and the given + * SpecificLongValue. + */ + public LongValue divide(SpecificLongValue other) + { + return this; + } + + /** + * Returns the quotient of the given SpecificLongValue and this + * LongValue. + */ + public LongValue divideOf(SpecificLongValue other) + { + return this; + } + + /** + * Returns the remainder of this LongValue divided by the given + * SpecificLongValue. + */ + public LongValue remainder(SpecificLongValue other) + { + return this; + } + + /** + * Returns the remainder of the given SpecificLongValue divided by this + * LongValue. + */ + public LongValue remainderOf(SpecificLongValue other) + { + return this; + } + + /** + * Returns this LongValue, shifted left by the given SpecificLongValue. + */ + public LongValue shiftLeft(SpecificLongValue other) + { + return this; + } + + /** + * Returns this LongValue, shifted right by the given SpecificLongValue. + */ + public LongValue shiftRight(SpecificLongValue other) + { + return this; + } + + /** + * Returns this unsigned LongValue, shifted right by the given + * SpecificLongValue. + */ + public LongValue unsignedShiftRight(SpecificLongValue other) + { + return this; + } + + /** + * Returns the logical and of this LongValue and the given + * SpecificLongValue. + */ + public LongValue and(SpecificLongValue other) + { + return this; + } + + /** + * Returns the logical or of this LongValue and the given + * SpecificLongValue. + */ + public LongValue or(SpecificLongValue other) + { + return this; + } + + /** + * Returns the logical xor of this LongValue and the given + * SpecificLongValue. + */ + public LongValue xor(SpecificLongValue other) + { + return this; + } + + /** + * Returns an IntegerValue with value -1, 0, or 1, if this LongValue is + * less than, equal to, or greater than the given SpecificLongValue, + * respectively. + */ + public IntegerValue compare(SpecificLongValue other) + { + return new ComparisonValue(this, other); + } + + + // Derived binary methods. + + /** + * Returns an IntegerValue with value 1, 0, or -1, if this LongValue is + * less than, equal to, or greater than the given SpecificLongValue, + * respectively. + */ + public final IntegerValue compareReverse(SpecificLongValue other) + { + return compare(other).negate(); + } + + + // Similar binary methods, but this time with particular arguments. + + /** + * Returns the generalization of this LongValue and the given other + * ParticularLongValue. + */ + public LongValue generalize(ParticularLongValue other) + { + return generalize((SpecificLongValue)other); + } + + + /** + * Returns the sum of this LongValue and the given ParticularLongValue. + */ + public LongValue add(ParticularLongValue other) + { + return add((SpecificLongValue)other); + } + + /** + * Returns the difference of this LongValue and the given ParticularLongValue. + */ + public LongValue subtract(ParticularLongValue other) + { + return subtract((SpecificLongValue)other); + } + + /** + * Returns the difference of the given ParticularLongValue and this LongValue. + */ + public LongValue subtractFrom(ParticularLongValue other) + { + return subtractFrom((SpecificLongValue)other); + } + + /** + * Returns the product of this LongValue and the given ParticularLongValue. + */ + public LongValue multiply(ParticularLongValue other) + { + return multiply((SpecificLongValue)other); + } + + /** + * Returns the quotient of this LongValue and the given + * ParticularLongValue. + */ + public LongValue divide(ParticularLongValue other) + { + return divide((SpecificLongValue)other); + } + + /** + * Returns the quotient of the given ParticularLongValue and this + * LongValue. + */ + public LongValue divideOf(ParticularLongValue other) + { + return divideOf((SpecificLongValue)other); + } + + /** + * Returns the remainder of this LongValue divided by the given + * ParticularLongValue. + */ + public LongValue remainder(ParticularLongValue other) + { + return remainder((SpecificLongValue)other); + } + + /** + * Returns the remainder of the given ParticularLongValue divided by this + * LongValue. + */ + public LongValue remainderOf(ParticularLongValue other) + { + return remainderOf((SpecificLongValue)other); + } + + /** + * Returns this LongValue, shifted left by the given ParticularIntegerValue. + */ + public LongValue shiftLeft(ParticularIntegerValue other) + { + return shiftLeft((SpecificIntegerValue)other); + } + + /** + * Returns this LongValue, shifted right by the given ParticularIntegerValue. + */ + public LongValue shiftRight(ParticularIntegerValue other) + { + return shiftRight((SpecificIntegerValue)other); + } + + /** + * Returns this unsigned LongValue, shifted right by the given + * ParticularIntegerValue. + */ + public LongValue unsignedShiftRight(ParticularIntegerValue other) + { + return unsignedShiftRight((SpecificIntegerValue)other); + } + + /** + * Returns the logical and of this LongValue and the given + * ParticularLongValue. + */ + public LongValue and(ParticularLongValue other) + { + return and((SpecificLongValue)other); + } + + /** + * Returns the logical or of this LongValue and the given + * ParticularLongValue. + */ + public LongValue or(ParticularLongValue other) + { + return or((SpecificLongValue)other); + } + + /** + * Returns the logical xor of this LongValue and the given + * ParticularLongValue. + */ + public LongValue xor(ParticularLongValue other) + { + return xor((SpecificLongValue)other); + } + + /** + * Returns an IntegerValue with value -1, 0, or 1, if this LongValue is + * less than, equal to, or greater than the given ParticularLongValue, + * respectively. + */ + public IntegerValue compare(ParticularLongValue other) + { + return compare((SpecificLongValue)other); + } + + + // Derived binary methods. + + /** + * Returns an IntegerValue with value 1, 0, or -1, if this LongValue is + * less than, equal to, or greater than the given ParticularLongValue, + * respectively. + */ + public final IntegerValue compareReverse(ParticularLongValue other) + { + return compare(other).negate(); + } + + + // Implementations for Value. + + public final LongValue longValue() + { + return this; + } + + public final Value generalize(Value other) + { + return this.generalize(other.longValue()); + } + + public final int computationalType() + { + return TYPE_LONG; + } + + public final String internalType() + { + return String.valueOf(ClassConstants.INTERNAL_TYPE_INT); + } +} diff --git a/src/proguard/evaluation/value/NegatedDoubleValue.java b/src/proguard/evaluation/value/NegatedDoubleValue.java new file mode 100644 index 000000000..7619be776 --- /dev/null +++ b/src/proguard/evaluation/value/NegatedDoubleValue.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 DoubleValue represents a double value that is negated. + * + * @author Eric Lafortune + */ +final class NegatedDoubleValue extends SpecificDoubleValue +{ + private final DoubleValue doubleValue; + + + /** + * Creates a new negated double value of the given double value. + */ + public NegatedDoubleValue(DoubleValue doubleValue) + { + this.doubleValue = doubleValue; + } + + + // Implementations of unary methods of DoubleValue. + + public DoubleValue negate() + { + return doubleValue; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.doubleValue.equals(((NegatedDoubleValue)object).doubleValue); + } + + + public int hashCode() + { + return super.hashCode() ^ + doubleValue.hashCode(); + } + + + public String toString() + { + return "-"+doubleValue; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/NegatedFloatValue.java b/src/proguard/evaluation/value/NegatedFloatValue.java new file mode 100644 index 000000000..51b50747d --- /dev/null +++ b/src/proguard/evaluation/value/NegatedFloatValue.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 FloatValue represents a float value that is negated. + * + * @author Eric Lafortune + */ +final class NegatedFloatValue extends SpecificFloatValue +{ + private final FloatValue floatValue; + + + /** + * Creates a new negated float value of the given float value. + */ + public NegatedFloatValue(FloatValue floatValue) + { + this.floatValue = floatValue; + } + + + // Implementations of unary methods of FloatValue. + + public FloatValue negate() + { + return floatValue; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.floatValue.equals(((NegatedFloatValue)object).floatValue); + } + + + public int hashCode() + { + return super.hashCode() ^ + floatValue.hashCode(); + } + + + public String toString() + { + return "-"+floatValue; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/NegatedIntegerValue.java b/src/proguard/evaluation/value/NegatedIntegerValue.java new file mode 100644 index 000000000..17290836e --- /dev/null +++ b/src/proguard/evaluation/value/NegatedIntegerValue.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents a integer value that is negated. + * + * @author Eric Lafortune + */ +final class NegatedIntegerValue extends SpecificIntegerValue +{ + private final IntegerValue integerValue; + + + /** + * Creates a new negated integer value of the given integer value. + */ + public NegatedIntegerValue(IntegerValue integerValue) + { + this.integerValue = integerValue; + } + + + // Implementations of unary methods of IntegerValue. + + public IntegerValue negate() + { + return integerValue; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.integerValue.equals(((NegatedIntegerValue)object).integerValue); + } + + + public int hashCode() + { + return super.hashCode() ^ + integerValue.hashCode(); + } + + + public String toString() + { + return "-"+integerValue; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/NegatedLongValue.java b/src/proguard/evaluation/value/NegatedLongValue.java new file mode 100644 index 000000000..75105243f --- /dev/null +++ b/src/proguard/evaluation/value/NegatedLongValue.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 LongValue represents a long value that is negated. + * + * @author Eric Lafortune + */ +final class NegatedLongValue extends SpecificLongValue +{ + private final LongValue longValue; + + + /** + * Creates a new negated long value of the given long value. + */ + public NegatedLongValue(LongValue longValue) + { + this.longValue = longValue; + } + + + // Implementations of unary methods of LongValue. + + public LongValue negate() + { + return longValue; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return this == object || + super.equals(object) && + this.longValue.equals(((NegatedLongValue)object).longValue); + } + + + public int hashCode() + { + return super.hashCode() ^ + longValue.hashCode(); + } + + + public String toString() + { + return "-"+longValue; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ParticularDoubleValue.java b/src/proguard/evaluation/value/ParticularDoubleValue.java new file mode 100644 index 000000000..e8c5aa708 --- /dev/null +++ b/src/proguard/evaluation/value/ParticularDoubleValue.java @@ -0,0 +1,221 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 DoubleValue represents a particular double value. + * + * @author Eric Lafortune + */ +final class ParticularDoubleValue extends SpecificDoubleValue +{ + private final double value; + + + /** + * Creates a new particular double value. + */ + public ParticularDoubleValue(double value) + { + this.value = value; + } + + + // Implementations for DoubleValue. + + public double value() + { + return value; + } + + + // Implementations of unary methods of DoubleValue. + + public DoubleValue negate() + { + return new ParticularDoubleValue(-value); + } + + public IntegerValue convertToInteger() + { + return new ParticularIntegerValue((int)value); + } + + public LongValue convertToLong() + { + return new ParticularLongValue((long)value); + } + + public FloatValue convertToFloat() + { + return new ParticularFloatValue((float)value); + } + + + // Implementations of binary methods of DoubleValue. + + public DoubleValue generalize(DoubleValue other) + { + return other.generalize(this); + } + + public DoubleValue add(DoubleValue other) + { + // Careful: -0.0 + 0.0 == 0.0 + //return value == 0.0 ? other : other.add(this); + return other.add(this); + } + + public DoubleValue subtract(DoubleValue other) + { + // Careful: -0.0 + 0.0 == 0.0 + //return value == 0.0 ? other.negate() : other.subtractFrom(this); + return other.subtractFrom(this); + } + + public DoubleValue subtractFrom(DoubleValue other) + { + // Careful: -0.0 + 0.0 == 0.0 + //return value == 0.0 ? other : other.subtract(this); + return other.subtract(this); + } + + public DoubleValue multiply(DoubleValue other) + { + return other.multiply(this); + } + + public DoubleValue divide(DoubleValue other) + { + return other.divideOf(this); + } + + public DoubleValue divideOf(DoubleValue other) + { + return other.divide(this); + } + + public DoubleValue remainder(DoubleValue other) + { + return other.remainderOf(this); + } + + public DoubleValue remainderOf(DoubleValue other) + { + return other.remainder(this); + } + + public IntegerValue compare(DoubleValue other) + { + return other.compareReverse(this); + } + + + // Implementations of binary DoubleValue methods with ParticularDoubleValue + // arguments. + + public DoubleValue generalize(ParticularDoubleValue other) + { + // Also handle NaN and Infinity. + return Double.doubleToRawLongBits(this.value) == + Double.doubleToRawLongBits(other.value) ? + this : ValueFactory.DOUBLE_VALUE; + } + + public DoubleValue add(ParticularDoubleValue other) + { + return new ParticularDoubleValue(this.value + other.value); + } + + public DoubleValue subtract(ParticularDoubleValue other) + { + return new ParticularDoubleValue(this.value - other.value); + } + + public DoubleValue subtractFrom(ParticularDoubleValue other) + { + return new ParticularDoubleValue(other.value - this.value); + } + + public DoubleValue multiply(ParticularDoubleValue other) + { + return new ParticularDoubleValue(this.value * other.value); + } + + public DoubleValue divide(ParticularDoubleValue other) + { + return new ParticularDoubleValue(this.value / other.value); + } + + public DoubleValue divideOf(ParticularDoubleValue other) + { + return new ParticularDoubleValue(other.value / this.value); + } + + public DoubleValue remainder(ParticularDoubleValue other) + { + return new ParticularDoubleValue(this.value % other.value); + } + + public DoubleValue remainderOf(ParticularDoubleValue other) + { + return new ParticularDoubleValue(other.value % this.value); + } + + public IntegerValue compare(ParticularDoubleValue other) + { + return this.value < other.value ? SpecificValueFactory.INTEGER_VALUE_M1 : + this.value == other.value ? SpecificValueFactory.INTEGER_VALUE_0 : + SpecificValueFactory.INTEGER_VALUE_1; + } + + + // Implementations for Value. + + public boolean isParticular() + { + return true; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + // Also handle NaN and Infinity. + return super.equals(object) && + Double.doubleToLongBits(this.value) == + Double.doubleToLongBits(((ParticularDoubleValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + (int)Double.doubleToLongBits(value); + } + + + public String toString() + { + return value+"d"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ParticularFloatValue.java b/src/proguard/evaluation/value/ParticularFloatValue.java new file mode 100644 index 000000000..cbdde310c --- /dev/null +++ b/src/proguard/evaluation/value/ParticularFloatValue.java @@ -0,0 +1,221 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 FloatValue represents a particular float value. + * + * @author Eric Lafortune + */ +final class ParticularFloatValue extends SpecificFloatValue +{ + private final float value; + + + /** + * Creates a new particular float value. + */ + public ParticularFloatValue(float value) + { + this.value = value; + } + + + // Implementations for FloatValue. + + public float value() + { + return value; + } + + + // Implementations of unary methods of FloatValue. + + public FloatValue negate() + { + return new ParticularFloatValue(-value); + } + + public IntegerValue convertToInteger() + { + return new ParticularIntegerValue((int)value); + } + + public LongValue convertToLong() + { + return new ParticularLongValue((long)value); + } + + public DoubleValue convertToDouble() + { + return new ParticularDoubleValue((float)value); + } + + + // Implementations of binary methods of FloatValue. + + public FloatValue generalize(FloatValue other) + { + return other.generalize(this); + } + + public FloatValue add(FloatValue other) + { + // Careful: -0.0 + 0.0 == 0.0 + //return value == 0.0 ? other : other.add(this); + return other.add(this); + } + + public FloatValue subtract(FloatValue other) + { + // Careful: -0.0 + 0.0 == 0.0 + //return value == 0.0 ? other.negate() : other.subtractFrom(this); + return other.subtractFrom(this); + } + + public FloatValue subtractFrom(FloatValue other) + { + // Careful: -0.0 + 0.0 == 0.0 + //return value == 0.0 ? other : other.subtract(this); + return other.subtract(this); + } + + public FloatValue multiply(FloatValue other) + { + return other.multiply(this); + } + + public FloatValue divide(FloatValue other) + { + return other.divideOf(this); + } + + public FloatValue divideOf(FloatValue other) + { + return other.divide(this); + } + + public FloatValue remainder(FloatValue other) + { + return other.remainderOf(this); + } + + public FloatValue remainderOf(FloatValue other) + { + return other.remainder(this); + } + + public IntegerValue compare(FloatValue other) + { + return other.compareReverse(this); + } + + + // Implementations of binary FloatValue methods with ParticularFloatValue + // arguments. + + public FloatValue generalize(ParticularFloatValue other) + { + // Also handle NaN and Infinity. + return Float.floatToRawIntBits(this.value) == + Float.floatToRawIntBits(other.value) ? + this : ValueFactory.FLOAT_VALUE; + } + + public FloatValue add(ParticularFloatValue other) + { + return new ParticularFloatValue(this.value + other.value); + } + + public FloatValue subtract(ParticularFloatValue other) + { + return new ParticularFloatValue(this.value - other.value); + } + + public FloatValue subtractFrom(ParticularFloatValue other) + { + return new ParticularFloatValue(other.value - this.value); + } + + public FloatValue multiply(ParticularFloatValue other) + { + return new ParticularFloatValue(this.value * other.value); + } + + public FloatValue divide(ParticularFloatValue other) + { + return new ParticularFloatValue(this.value / other.value); + } + + public FloatValue divideOf(ParticularFloatValue other) + { + return new ParticularFloatValue(other.value / this.value); + } + + public FloatValue remainder(ParticularFloatValue other) + { + return new ParticularFloatValue(this.value % other.value); + } + + public FloatValue remainderOf(ParticularFloatValue other) + { + return new ParticularFloatValue(other.value % this.value); + } + + public IntegerValue compare(ParticularFloatValue other) + { + return this.value < other.value ? SpecificValueFactory.INTEGER_VALUE_M1 : + this.value == other.value ? SpecificValueFactory.INTEGER_VALUE_0 : + SpecificValueFactory.INTEGER_VALUE_1; + } + + + // Implementations for Value. + + public boolean isParticular() + { + return true; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + // Also handle NaN and Infinity. + return super.equals(object) && + Float.floatToIntBits(this.value) == + Float.floatToIntBits(((ParticularFloatValue)object).value); + } + + + public int hashCode() + { + return super.hashCode() ^ + Float.floatToIntBits(value); + } + + + public String toString() + { + return value+"f"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ParticularIntegerValue.java b/src/proguard/evaluation/value/ParticularIntegerValue.java new file mode 100644 index 000000000..609e95fbb --- /dev/null +++ b/src/proguard/evaluation/value/ParticularIntegerValue.java @@ -0,0 +1,383 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents a particular integer value. + * + * @author Eric Lafortune + */ +final class ParticularIntegerValue extends SpecificIntegerValue +{ + private final int value; + + + /** + * Creates a new particular integer value. + */ + public ParticularIntegerValue(int value) + { + this.value = value; + } + + + // Implementations for IntegerValue. + + public int value() + { + return value; + } + + + // Implementations of unary methods of IntegerValue. + + public IntegerValue negate() + { + return new ParticularIntegerValue(-value); + } + + public IntegerValue convertToByte() + { + int byteValue = (byte)value; + + return byteValue == value ? + this : + new ParticularIntegerValue(byteValue); + } + + public IntegerValue convertToCharacter() + { + int charValue = (char)value; + + return charValue == value ? + this : + new ParticularIntegerValue(charValue); + } + + public IntegerValue convertToShort() + { + int shortValue = (short)value; + + return shortValue == value ? + this : + new ParticularIntegerValue(shortValue); + } + + public LongValue convertToLong() + { + return new ParticularLongValue((long)value); + } + + public FloatValue convertToFloat() + { + return new ParticularFloatValue((float)value); + } + + public DoubleValue convertToDouble() + { + return new ParticularDoubleValue((double)value); + } + + + // Implementations of binary methods of IntegerValue. + + public IntegerValue generalize(IntegerValue other) + { + return other.generalize(this); + } + + public IntegerValue add(IntegerValue other) + { + return other.add(this); + } + + public IntegerValue subtract(IntegerValue other) + { + return other.subtractFrom(this); + } + + public IntegerValue subtractFrom(IntegerValue other) + { + return other.subtract(this); + } + + public IntegerValue multiply(IntegerValue other) + { + return other.multiply(this); + } + + public IntegerValue divide(IntegerValue other) + throws ArithmeticException + { + return other.divideOf(this); + } + + public IntegerValue divideOf(IntegerValue other) + throws ArithmeticException + { + return other.divide(this); + } + + public IntegerValue remainder(IntegerValue other) + throws ArithmeticException + { + return other.remainderOf(this); + } + + public IntegerValue remainderOf(IntegerValue other) + throws ArithmeticException + { + return other.remainder(this); + } + + public IntegerValue shiftLeft(IntegerValue other) + { + return other.shiftLeftOf(this); + } + + public IntegerValue shiftLeftOf(IntegerValue other) + { + return other.shiftLeft(this); + } + + public IntegerValue shiftRight(IntegerValue other) + { + return other.shiftRightOf(this); + } + + public IntegerValue shiftRightOf(IntegerValue other) + { + return other.shiftRight(this); + } + + public IntegerValue unsignedShiftRight(IntegerValue other) + { + return other.unsignedShiftRightOf(this); + } + + public IntegerValue unsignedShiftRightOf(IntegerValue other) + { + return other.unsignedShiftRight(this); + } + + public LongValue shiftLeftOf(LongValue other) + { + return other.shiftLeft(this); + } + + public LongValue shiftRightOf(LongValue other) + { + return other.shiftRight(this); + } + + public LongValue unsignedShiftRightOf(LongValue other) + { + return other.unsignedShiftRight(this); + } + + public IntegerValue and(IntegerValue other) + { + return other.and(this); + } + + public IntegerValue or(IntegerValue other) + { + return other.or(this); + } + + public IntegerValue xor(IntegerValue other) + { + return other.xor(this); + } + + public int equal(IntegerValue other) + { + return other.equal(this); + } + + public int lessThan(IntegerValue other) + { + return other.greaterThan(this); + } + + public int lessThanOrEqual(IntegerValue other) + { + return other.greaterThanOrEqual(this); + } + + + // Implementations of binary IntegerValue methods with ParticularIntegerValue + // arguments. + + public IntegerValue generalize(ParticularIntegerValue other) + { + return generalize((SpecificIntegerValue)other); + } + + public IntegerValue add(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value + other.value); + } + + public IntegerValue subtract(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value - other.value); + } + + public IntegerValue subtractFrom(ParticularIntegerValue other) + { + return new ParticularIntegerValue(other.value - this.value); + } + + public IntegerValue multiply(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value * other.value); + } + + public IntegerValue divide(ParticularIntegerValue other) + throws ArithmeticException + { + return new ParticularIntegerValue(this.value / other.value); + } + + public IntegerValue divideOf(ParticularIntegerValue other) + throws ArithmeticException + { + return new ParticularIntegerValue(other.value / this.value); + } + + public IntegerValue remainder(ParticularIntegerValue other) + throws ArithmeticException + { + return new ParticularIntegerValue(this.value % other.value); + } + + public IntegerValue remainderOf(ParticularIntegerValue other) + throws ArithmeticException + { + return new ParticularIntegerValue(other.value % this.value); + } + + public IntegerValue shiftLeft(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value << other.value); + } + + public IntegerValue shiftRight(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value >> other.value); + } + + public IntegerValue unsignedShiftRight(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value >>> other.value); + } + + public IntegerValue shiftLeftOf(ParticularIntegerValue other) + { + return new ParticularIntegerValue(other.value << this.value); + } + + public IntegerValue shiftRightOf(ParticularIntegerValue other) + { + return new ParticularIntegerValue(other.value >> this.value); + } + + public IntegerValue unsignedShiftRightOf(ParticularIntegerValue other) + { + return new ParticularIntegerValue(other.value >>> this.value); + } + + public LongValue shiftLeftOf(ParticularLongValue other) + { + return new ParticularLongValue(other.value() << this.value); + } + + public LongValue shiftRightOf(ParticularLongValue other) + { + return new ParticularLongValue(other.value() >> this.value); + } + + public LongValue unsignedShiftRightOf(ParticularLongValue other) + { + return new ParticularLongValue(other.value() >>> this.value); + } + + public IntegerValue and(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value & other.value); + } + + public IntegerValue or(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value | other.value); + } + + public IntegerValue xor(ParticularIntegerValue other) + { + return new ParticularIntegerValue(this.value ^ other.value); + } + + public int equal(ParticularIntegerValue other) + { + return this.value == other.value ? ALWAYS : NEVER; + } + + public int lessThan(ParticularIntegerValue other) + { + return this.value < other.value ? ALWAYS : NEVER; + } + + public int lessThanOrEqual(ParticularIntegerValue other) + { + return this.value <= other.value ? ALWAYS : NEVER; + } + + + // Implementations for Value. + + public boolean isParticular() + { + return true; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return super.equals(object) && + this.value == ((ParticularIntegerValue)object).value; + } + + + public int hashCode() + { + return this.getClass().hashCode() ^ + value; + } + + + public String toString() + { + return Integer.toString(value); + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ParticularLongValue.java b/src/proguard/evaluation/value/ParticularLongValue.java new file mode 100644 index 000000000..190323516 --- /dev/null +++ b/src/proguard/evaluation/value/ParticularLongValue.java @@ -0,0 +1,271 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 LongValue represents a particular long value. + * + * @author Eric Lafortune + */ +final class ParticularLongValue extends SpecificLongValue +{ + private final long value; + + + /** + * Creates a new particular long value. + */ + public ParticularLongValue(long value) + { + this.value = value; + } + + + // Implementations for LongValue. + + public long value() + { + return value; + } + + + // Implementations of unary methods of LongValue. + + public LongValue negate() + { + return new ParticularLongValue(-value); + } + + public IntegerValue convertToInteger() + { + return new ParticularIntegerValue((int)value); + } + + public FloatValue convertToFloat() + { + return new ParticularFloatValue((float)value); + } + + public DoubleValue convertToDouble() + { + return new ParticularDoubleValue((double)value); + } + + + // Implementations of binary methods of LongValue. + + public LongValue generalize(LongValue other) + { + return other.generalize(this); + } + + public LongValue add(LongValue other) + { + return other.add(this); + } + + public LongValue subtract(LongValue other) + { + return other.subtractFrom(this); + } + + public LongValue subtractFrom(LongValue other) + { + return other.subtract(this); + } + + public LongValue multiply(LongValue other) + { + return other.multiply(this); + } + + public LongValue divide(LongValue other) + throws ArithmeticException + { + return other.divideOf(this); + } + + public LongValue divideOf(LongValue other) + throws ArithmeticException + { + return other.divide(this); + } + + public LongValue remainder(LongValue other) + throws ArithmeticException + { + return other.remainderOf(this); + } + + public LongValue remainderOf(LongValue other) + throws ArithmeticException + { + return other.remainder(this); + } + + public LongValue shiftLeft(IntegerValue other) + { + return other.shiftLeftOf(this); + } + + public LongValue shiftRight(IntegerValue other) + { + return other.shiftRightOf(this); + } + + public LongValue unsignedShiftRight(IntegerValue other) + { + return other.unsignedShiftRightOf(this); + } + + public LongValue and(LongValue other) + { + return other.and(this); + } + + public LongValue or(LongValue other) + { + return other.or(this); + } + + public LongValue xor(LongValue other) + { + return other.xor(this); + } + + public IntegerValue compare(LongValue other) + { + return other.compareReverse(this); + } + + + // Implementations of binary LongValue methods with ParticularLongValue + // arguments. + + public LongValue generalize(ParticularLongValue other) + { + return generalize((SpecificLongValue)other); + } + + public LongValue add(ParticularLongValue other) + { + return new ParticularLongValue(this.value + other.value); + } + + public LongValue subtract(ParticularLongValue other) + { + return new ParticularLongValue(this.value - other.value); + } + + public LongValue subtractFrom(ParticularLongValue other) + { + return new ParticularLongValue(other.value - this.value); + } + + public LongValue multiply(ParticularLongValue other) + { + return new ParticularLongValue(this.value * other.value); + } + + public LongValue divide(ParticularLongValue other) + throws ArithmeticException + { + return new ParticularLongValue(this.value / other.value); + } + + public LongValue divideOf(ParticularLongValue other) + throws ArithmeticException + { + return new ParticularLongValue(other.value / this.value); + } + + public LongValue remainder(ParticularLongValue other) + throws ArithmeticException + { + return new ParticularLongValue(this.value % other.value); + } + + public LongValue remainderOf(ParticularLongValue other) + throws ArithmeticException + { + return new ParticularLongValue(other.value % this.value); + } + + public LongValue shiftLeft(ParticularIntegerValue other) + { + return new ParticularLongValue(this.value << other.value()); + } + + public LongValue shiftRight(ParticularIntegerValue other) + { + return new ParticularLongValue(this.value >> other.value()); + } + + public LongValue unsignedShiftRight(ParticularIntegerValue other) + { + return new ParticularLongValue(this.value >>> other.value()); + } + + public LongValue and(ParticularLongValue other) + { + return new ParticularLongValue(this.value & other.value); + } + + public LongValue or(ParticularLongValue other) + { + return new ParticularLongValue(this.value | other.value); + } + + public LongValue xor(ParticularLongValue other) + { + return new ParticularLongValue(this.value ^ other.value); + } + + + // Implementations for Value. + + public boolean isParticular() + { + return true; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return super.equals(object) && + this.value == ((ParticularLongValue)object).value; + } + + + public int hashCode() + { + return this.getClass().hashCode() ^ + (int)value; + } + + + public String toString() + { + return value+"L"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ReferenceValue.java b/src/proguard/evaluation/value/ReferenceValue.java new file mode 100644 index 000000000..4a52e82ad --- /dev/null +++ b/src/proguard/evaluation/value/ReferenceValue.java @@ -0,0 +1,540 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 null. If + * the type is null, the value is null. + * + * @author Eric Lafortune + */ +public class ReferenceValue extends Category1Value +{ + private static final boolean DEBUG = false; + + + protected final String type; + protected final Clazz referencedClass; + protected 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 null. + */ + 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) + { + // If both types are identical, the generalization is the same too. + if (this.equals(other)) + { + return this; + } + + 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)); + + int thisSuperClassesCount = thisSuperClasses.size(); + if (thisSuperClassesCount == 0 && + thisReferencedClass.getSuperName() != null) + { + throw new IllegalArgumentException("Can't find any super classes of ["+thisType+"] (not even immediate super class ["+thisReferencedClass.getSuperName()+"])"); + } + + // Collect the superclasses and interfaces of the other class. + Set otherSuperClasses = new HashSet(); + otherReferencedClass.hierarchyAccept(false, true, true, false, + new ClassCollector(otherSuperClasses)); + + int otherSuperClassesCount = otherSuperClasses.size(); + if (otherSuperClassesCount == 0 && + otherReferencedClass.getSuperName() != null) + { + throw new IllegalArgumentException("Can't find any super classes of ["+otherType+"] (not even immediate super class ["+otherReferencedClass.getSuperName()+"])"); + } + + 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 commonClass = 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 && + commonClass != null && + commonClass.getName().compareTo(commonSuperClass.getName()) > 0)) + { + commonClass = commonSuperClass; + maximumSuperClassCount = superClassCount; + } + } + + if (commonClass == null) + { + throw new IllegalArgumentException("Can't find common super class of ["+ + thisType +"] (with "+thisSuperClassesCount +" known super classes) and ["+ + otherType+"] (with "+otherSuperClassesCount+" known super classes)"); + } + + if (DEBUG) + { + System.out.println(" Best common class: ["+commonClass.getName()+"]"); + } + + // TODO: Handle more difficult cases, with multiple global subclasses. + + return new ReferenceValue(commonDimensionCount == 0 ? + commonClass.getName() : + ClassUtil.internalArrayTypeFromClassName(commonClass.getName(), + commonDimensionCount), + commonClass, + 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++; + } + } + + return count; + } + + + /** + * Returns whether this ReferenceValue is equal to the given other + * ReferenceValue. + * @return NEVER, MAYBE, or ALWAYS. + */ + public int equal(ReferenceValue other) + { + return this.type == null && other.type == null ? ALWAYS : MAYBE; + } + + + // Derived unary methods. + + /** + * Returns whether this ReferenceValue is not null. + * @return NEVER, MAYBE, or ALWAYS. + */ + 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 NEVER, MAYBE, or ALWAYS. + */ + 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 isParticular() + { + 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 (this == object) + { + return true; + } + + 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 type == null ? + "null" : + type + (referencedClass == null ? "?" : "") + (mayBeNull ? "" : "!"); + } +} diff --git a/src/proguard/evaluation/value/SpecificDoubleValue.java b/src/proguard/evaluation/value/SpecificDoubleValue.java new file mode 100644 index 000000000..644f5534b --- /dev/null +++ b/src/proguard/evaluation/value/SpecificDoubleValue.java @@ -0,0 +1,186 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 DoubleValue represents a specific double value. + * + * @author Eric Lafortune + */ +abstract class SpecificDoubleValue extends DoubleValue +{ + // Implementations of unary methods of DoubleValue. + + public DoubleValue negate() + { + return new NegatedDoubleValue(this); + } + + public IntegerValue convertToInteger() + { + return new ConvertedIntegerValue(this); + } + + public LongValue convertToLong() + { + return new ConvertedLongValue(this); + } + + public FloatValue convertToFloat() + { + return new ConvertedFloatValue(this); + } + + + // Implementations of binary methods of DoubleValue. + + public DoubleValue generalize(DoubleValue other) + { + return other.generalize(this); + } + + public DoubleValue add(DoubleValue other) + { + return other.add(this); + } + + public DoubleValue subtract(DoubleValue other) + { + return other.subtractFrom(this); + } + + public DoubleValue subtractFrom(DoubleValue other) + { + return other.subtract(this); + } + + public DoubleValue multiply(DoubleValue other) + { + return other.multiply(this); + } + + public DoubleValue divide(DoubleValue other) + { + return other.divideOf(this); + } + + public DoubleValue divideOf(DoubleValue other) + { + return other.divide(this); + } + + public DoubleValue remainder(DoubleValue other) + { + return other.remainderOf(this); + } + + public DoubleValue remainderOf(DoubleValue other) + { + return other.remainder(this); + } + + public IntegerValue compare(DoubleValue other) + { + return other.compareReverse(this); + } + + + // Implementations of binary DoubleValue methods with SpecificDoubleValue + // arguments. + + public DoubleValue generalize(SpecificDoubleValue other) + { + return this.equals(other) ? this : ValueFactory.DOUBLE_VALUE; + } + + public DoubleValue add(SpecificDoubleValue other) + { + return new CompositeDoubleValue(this, CompositeDoubleValue.ADD, other); + } + + public DoubleValue subtract(SpecificDoubleValue other) + { + return new CompositeDoubleValue(this, CompositeDoubleValue.SUBTRACT, other); + } + + public DoubleValue subtractFrom(SpecificDoubleValue other) + { + return new CompositeDoubleValue(other, CompositeDoubleValue.SUBTRACT, this); + } + + public DoubleValue multiply(SpecificDoubleValue other) + { + return new CompositeDoubleValue(this, CompositeDoubleValue.MULTIPLY, other); + } + + public DoubleValue divide(SpecificDoubleValue other) + { + return new CompositeDoubleValue(this, CompositeDoubleValue.DIVIDE, other); + } + + public DoubleValue divideOf(SpecificDoubleValue other) + { + return new CompositeDoubleValue(other, CompositeDoubleValue.DIVIDE, this); + } + + public DoubleValue remainder(SpecificDoubleValue other) + { + return new CompositeDoubleValue(this, CompositeDoubleValue.REMAINDER, other); + } + + public DoubleValue remainderOf(SpecificDoubleValue other) + { + return new CompositeDoubleValue(other, CompositeDoubleValue.REMAINDER, this); + } + + public IntegerValue compare(SpecificDoubleValue other) + { + return ValueFactory.INTEGER_VALUE; + + // Not handling NaN properly. + //return this.equals(other) ? + // SpecificValueFactory.INTEGER_VALUE_0 : + // new ComparisonValue(this, other); + } + + + // Implementations for Value. + + public boolean isSpecific() + { + return true; + } + + + // 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/evaluation/value/SpecificFloatValue.java b/src/proguard/evaluation/value/SpecificFloatValue.java new file mode 100644 index 000000000..d88baa34a --- /dev/null +++ b/src/proguard/evaluation/value/SpecificFloatValue.java @@ -0,0 +1,186 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 FloatValue represents a specific float value. + * + * @author Eric Lafortune + */ +abstract class SpecificFloatValue extends FloatValue +{ + // Implementations of unary methods of FloatValue. + + public FloatValue negate() + { + return new NegatedFloatValue(this); + } + + public IntegerValue convertToInteger() + { + return new ConvertedIntegerValue(this); + } + + public LongValue convertToLong() + { + return new ConvertedLongValue(this); + } + + public DoubleValue convertToDouble() + { + return new ConvertedDoubleValue(this); + } + + + // Implementations of binary methods of FloatValue. + + public FloatValue generalize(FloatValue other) + { + return other.generalize(this); + } + + public FloatValue add(FloatValue other) + { + return other.add(this); + } + + public FloatValue subtract(FloatValue other) + { + return other.subtractFrom(this); + } + + public FloatValue subtractFrom(FloatValue other) + { + return other.subtract(this); + } + + public FloatValue multiply(FloatValue other) + { + return other.multiply(this); + } + + public FloatValue divide(FloatValue other) + { + return other.divideOf(this); + } + + public FloatValue divideOf(FloatValue other) + { + return other.divide(this); + } + + public FloatValue remainder(FloatValue other) + { + return other.remainderOf(this); + } + + public FloatValue remainderOf(FloatValue other) + { + return other.remainder(this); + } + + public IntegerValue compare(FloatValue other) + { + return other.compareReverse(this); + } + + + // Implementations of binary FloatValue methods with SpecificFloatValue + // arguments. + + public FloatValue generalize(SpecificFloatValue other) + { + return this.equals(other) ? this : ValueFactory.FLOAT_VALUE; + } + + public FloatValue add(SpecificFloatValue other) + { + return new CompositeFloatValue(this, CompositeFloatValue.ADD, other); + } + + public FloatValue subtract(SpecificFloatValue other) + { + return new CompositeFloatValue(this, CompositeFloatValue.SUBTRACT, other); + } + + public FloatValue subtractFrom(SpecificFloatValue other) + { + return new CompositeFloatValue(other, CompositeFloatValue.SUBTRACT, this); + } + + public FloatValue multiply(SpecificFloatValue other) + { + return new CompositeFloatValue(this, CompositeFloatValue.MULTIPLY, other); + } + + public FloatValue divide(SpecificFloatValue other) + { + return new CompositeFloatValue(this, CompositeFloatValue.DIVIDE, other); + } + + public FloatValue divideOf(SpecificFloatValue other) + { + return new CompositeFloatValue(other, CompositeFloatValue.DIVIDE, this); + } + + public FloatValue remainder(SpecificFloatValue other) + { + return new CompositeFloatValue(this, CompositeFloatValue.REMAINDER, other); + } + + public FloatValue remainderOf(SpecificFloatValue other) + { + return new CompositeFloatValue(other, CompositeFloatValue.REMAINDER, this); + } + + public IntegerValue compare(SpecificFloatValue other) + { + return ValueFactory.INTEGER_VALUE; + + // Not handling NaN properly. + //return this.equals(other) ? + // SpecificValueFactory.INTEGER_VALUE_0 : + // new ComparisonValue(this, other); + } + + + // Implementations for Value. + + public boolean isSpecific() + { + return true; + } + + + // 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/evaluation/value/SpecificIntegerValue.java b/src/proguard/evaluation/value/SpecificIntegerValue.java new file mode 100644 index 000000000..81f864649 --- /dev/null +++ b/src/proguard/evaluation/value/SpecificIntegerValue.java @@ -0,0 +1,354 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 IntegerValue represents a specific integer value. + * + * @author Eric Lafortune + */ +abstract class SpecificIntegerValue extends IntegerValue +{ + // Implementations of unary methods of IntegerValue. + + public IntegerValue negate() + { + return new NegatedIntegerValue(this); + } + + public IntegerValue convertToByte() + { + return new ConvertedByteValue(this); + } + + public IntegerValue convertToCharacter() + { + return new ConvertedCharacterValue(this); + } + + public IntegerValue convertToShort() + { + return new ConvertedShortValue(this); + } + + public LongValue convertToLong() + { + return new ConvertedLongValue(this); + } + + public FloatValue convertToFloat() + { + return new ConvertedFloatValue(this); + } + + public DoubleValue convertToDouble() + { + return new ConvertedDoubleValue(this); + } + + + // Implementations of binary methods of IntegerValue. + + public IntegerValue generalize(IntegerValue other) + { + return other.generalize(this); + } + + public IntegerValue add(IntegerValue other) + { + return other.add(this); + } + + public IntegerValue subtract(IntegerValue other) + { + return other.subtractFrom(this); + } + + public IntegerValue subtractFrom(IntegerValue other) + { + return other.subtract(this); + } + + public IntegerValue multiply(IntegerValue other) + { + return other.multiply(this); + } + + public IntegerValue divide(IntegerValue other) + throws ArithmeticException + { + return other.divideOf(this); + } + + public IntegerValue divideOf(IntegerValue other) + throws ArithmeticException + { + return other.divide(this); + } + + public IntegerValue remainder(IntegerValue other) + throws ArithmeticException + { + return other.remainderOf(this); + } + + public IntegerValue remainderOf(IntegerValue other) + throws ArithmeticException + { + return other.remainder(this); + } + + public IntegerValue shiftLeft(IntegerValue other) + { + return other.shiftLeftOf(this); + } + + public IntegerValue shiftLeftOf(IntegerValue other) + { + return other.shiftLeft(this); + } + + public IntegerValue shiftRight(IntegerValue other) + { + return other.shiftRightOf(this); + } + + public IntegerValue shiftRightOf(IntegerValue other) + { + return other.shiftRight(this); + } + + public IntegerValue unsignedShiftRight(IntegerValue other) + { + return other.unsignedShiftRightOf(this); + } + + public IntegerValue unsignedShiftRightOf(IntegerValue other) + { + return other.unsignedShiftRight(this); + } + + public LongValue shiftLeftOf(LongValue other) + { + return other.shiftLeft(this); + } + + public LongValue shiftRightOf(LongValue other) + { + return other.shiftRight(this); + } + + public LongValue unsignedShiftRightOf(LongValue other) + { + return other.unsignedShiftRight(this); + } + + public IntegerValue and(IntegerValue other) + { + return other.and(this); + } + + public IntegerValue or(IntegerValue other) + { + return other.or(this); + } + + public IntegerValue xor(IntegerValue other) + { + return other.xor(this); + } + + public int equal(IntegerValue other) + { + return other.equal(this); + } + + public int lessThan(IntegerValue other) + { + return other.greaterThan(this); + } + + public int lessThanOrEqual(IntegerValue other) + { + return other.greaterThanOrEqual(this); + } + + + // Implementations of binary IntegerValue methods with SpecificIntegerValue + // arguments. + + public IntegerValue generalize(SpecificIntegerValue other) + { + return this.equals(other) ? this : ValueFactory.INTEGER_VALUE; + } + + public IntegerValue add(SpecificIntegerValue other) + { + return new CompositeIntegerValue(this, CompositeIntegerValue.ADD, other); + } + + public IntegerValue subtract(SpecificIntegerValue other) + { + return this.equals(other) ? + SpecificValueFactory.INTEGER_VALUE_0 : + new CompositeIntegerValue(this, CompositeIntegerValue.SUBTRACT, other); + } + + public IntegerValue subtractFrom(SpecificIntegerValue other) + { + return this.equals(other) ? + SpecificValueFactory.INTEGER_VALUE_0 : + new CompositeIntegerValue(other, CompositeIntegerValue.SUBTRACT, this); + } + + public IntegerValue multiply(SpecificIntegerValue other) + { + return new CompositeIntegerValue(this, CompositeIntegerValue.MULTIPLY, other); + } + + public IntegerValue divide(SpecificIntegerValue other) + throws ArithmeticException + { + return new CompositeIntegerValue(this, CompositeIntegerValue.DIVIDE, other); + } + + public IntegerValue divideOf(SpecificIntegerValue other) + throws ArithmeticException + { + return new CompositeIntegerValue(other, CompositeIntegerValue.DIVIDE, this); + } + + public IntegerValue remainder(SpecificIntegerValue other) + throws ArithmeticException + { + return new CompositeIntegerValue(this, CompositeIntegerValue.REMAINDER, other); + } + + public IntegerValue remainderOf(SpecificIntegerValue other) + throws ArithmeticException + { + return new CompositeIntegerValue(other, CompositeIntegerValue.REMAINDER, this); + } + + public IntegerValue shiftLeft(SpecificIntegerValue other) + { + return new CompositeIntegerValue(this, CompositeIntegerValue.SHIFT_LEFT, other); + } + + public IntegerValue shiftRight(SpecificIntegerValue other) + { + return new CompositeIntegerValue(this, CompositeIntegerValue.SHIFT_RIGHT, other); + } + + public IntegerValue unsignedShiftRight(SpecificIntegerValue other) + { + return new CompositeIntegerValue(this, CompositeIntegerValue.UNSIGNED_SHIFT_RIGHT, other); + } + + public IntegerValue shiftLeftOf(SpecificIntegerValue other) + { + return new CompositeIntegerValue(other, CompositeIntegerValue.SHIFT_LEFT, this); + } + + public IntegerValue shiftRightOf(SpecificIntegerValue other) + { + return new CompositeIntegerValue(other, CompositeIntegerValue.SHIFT_RIGHT, this); + } + + public IntegerValue unsignedShiftRightOf(SpecificIntegerValue other) + { + return new CompositeIntegerValue(other, CompositeIntegerValue.UNSIGNED_SHIFT_RIGHT, this); + } + + public LongValue shiftLeftOf(SpecificLongValue other) + { + return new CompositeLongValue(other, CompositeLongValue.SHIFT_LEFT, this); + } + + public LongValue shiftRightOf(SpecificLongValue other) + { + return new CompositeLongValue(other, CompositeLongValue.SHIFT_RIGHT, this); + } + + public LongValue unsignedShiftRightOf(SpecificLongValue other) + { + return new CompositeLongValue(other, CompositeLongValue.UNSIGNED_SHIFT_RIGHT, this); + } + + public IntegerValue and(SpecificIntegerValue other) + { + return this.equals(other) ? + this : + new CompositeIntegerValue(other, CompositeIntegerValue.AND, this); + } + + public IntegerValue or(SpecificIntegerValue other) + { + return this.equals(other) ? + this : + new CompositeIntegerValue(other, CompositeIntegerValue.OR, this); + } + + public IntegerValue xor(SpecificIntegerValue other) + { + return this.equals(other) ? + SpecificValueFactory.INTEGER_VALUE_0 : + new CompositeIntegerValue(other, CompositeIntegerValue.XOR, this); + } + + public int equal(SpecificIntegerValue other) + { + return this.equals(other) ? ALWAYS : MAYBE; + } + + public int lessThan(SpecificIntegerValue other) + { + return this.equals(other) ? NEVER : MAYBE; + } + + public int lessThanOrEqual(SpecificIntegerValue other) + { + return this.equals(other) ? ALWAYS : MAYBE; + } + + + // Implementations for Value. + + public boolean isSpecific() + { + return true; + } + + + // 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/evaluation/value/SpecificLongValue.java b/src/proguard/evaluation/value/SpecificLongValue.java new file mode 100644 index 000000000..15138b48c --- /dev/null +++ b/src/proguard/evaluation/value/SpecificLongValue.java @@ -0,0 +1,259 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 LongValue represents a specific long value. + * + * @author Eric Lafortune + */ +abstract class SpecificLongValue extends LongValue +{ + // Implementations of unary methods of LongValue. + + public LongValue negate() + { + return new NegatedLongValue(this); + } + + public IntegerValue convertToInteger() + { + return new ConvertedIntegerValue(this); + } + + public FloatValue convertToFloat() + { + return new ConvertedFloatValue(this); + } + + public DoubleValue convertToDouble() + { + return new ConvertedDoubleValue(this); + } + + + // Implementations of binary methods of LongValue. + + public LongValue generalize(LongValue other) + { + return other.generalize(this); + } + + public LongValue add(LongValue other) + { + return other.add(this); + } + + public LongValue subtract(LongValue other) + { + return other.subtractFrom(this); + } + + public LongValue subtractFrom(LongValue other) + { + return other.subtract(this); + } + + public LongValue multiply(LongValue other) + { + return other.multiply(this); + } + + public LongValue divide(LongValue other) + throws ArithmeticException + { + return other.divideOf(this); + } + + public LongValue divideOf(LongValue other) + throws ArithmeticException + { + return other.divide(this); + } + + public LongValue remainder(LongValue other) + throws ArithmeticException + { + return other.remainderOf(this); + } + + public LongValue remainderOf(LongValue other) + throws ArithmeticException + { + return other.remainder(this); + } + + public LongValue shiftLeft(IntegerValue other) + { + return other.shiftLeftOf(this); + } + + public LongValue shiftRight(IntegerValue other) + { + return other.shiftRightOf(this); + } + + public LongValue unsignedShiftRight(IntegerValue other) + { + return other.unsignedShiftRightOf(this); + } + + public LongValue and(LongValue other) + { + return other.and(this); + } + + public LongValue or(LongValue other) + { + return other.or(this); + } + + public LongValue xor(LongValue other) + { + return other.xor(this); + } + + public IntegerValue compare(LongValue other) + { + return other.compareReverse(this); + } + + + // Implementations of binary LongValue methods with SpecificLongValue + // arguments. + + public LongValue generalize(SpecificLongValue other) + { + return this.equals(other) ? this : ValueFactory.LONG_VALUE; + } + + public LongValue add(SpecificLongValue other) + { + return new CompositeLongValue(this, CompositeLongValue.ADD, other); + } + + public LongValue subtract(SpecificLongValue other) + { + return this.equals(other) ? + SpecificValueFactory.LONG_VALUE_0 : + new CompositeLongValue(this, CompositeLongValue.SUBTRACT, other); + } + + public LongValue subtractFrom(SpecificLongValue other) + { + return this.equals(other) ? + SpecificValueFactory.LONG_VALUE_0 : + new CompositeLongValue(other, CompositeLongValue.SUBTRACT, this); + } + + public LongValue multiply(SpecificLongValue other) + { + return new CompositeLongValue(this, CompositeLongValue.MULTIPLY, other); + } + + public LongValue divide(SpecificLongValue other) + throws ArithmeticException + { + return new CompositeLongValue(this, CompositeLongValue.DIVIDE, other); + } + + public LongValue divideOf(SpecificLongValue other) + throws ArithmeticException + { + return new CompositeLongValue(other, CompositeLongValue.DIVIDE, this); + } + + public LongValue remainder(SpecificLongValue other) + throws ArithmeticException + { + return new CompositeLongValue(this, CompositeLongValue.REMAINDER, other); + } + + public LongValue remainderOf(SpecificLongValue other) + throws ArithmeticException + { + return new CompositeLongValue(other, CompositeLongValue.REMAINDER, this); + } + + public LongValue shiftLeft(SpecificLongValue other) + { + return new CompositeLongValue(this, CompositeLongValue.SHIFT_LEFT, other); + } + + public LongValue shiftRight(SpecificLongValue other) + { + return new CompositeLongValue(this, CompositeLongValue.SHIFT_RIGHT, other); + } + + public LongValue unsignedShiftRight(SpecificLongValue other) + { + return new CompositeLongValue(this, CompositeLongValue.UNSIGNED_SHIFT_RIGHT, other); + } + + public LongValue and(SpecificLongValue other) + { + return this.equals(other) ? + this : + new CompositeLongValue(other, CompositeLongValue.AND, this); + } + + public LongValue or(SpecificLongValue other) + { + return this.equals(other) ? + this : + new CompositeLongValue(other, CompositeLongValue.OR, this); + } + + public LongValue xor(SpecificLongValue other) + { + return this.equals(other) ? + SpecificValueFactory.LONG_VALUE_0 : + new CompositeLongValue(other, CompositeLongValue.XOR, this); + } + + public IntegerValue compare(SpecificLongValue other) + { + return new ComparisonValue(this, other); + } + + + // Implementations for Value. + + public boolean isSpecific() + { + return true; + } + + + // 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/evaluation/value/SpecificValueFactory.java b/src/proguard/evaluation/value/SpecificValueFactory.java new file mode 100644 index 000000000..f7619387f --- /dev/null +++ b/src/proguard/evaluation/value/SpecificValueFactory.java @@ -0,0 +1,97 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ParticularIntegerValue(-1); + static final IntegerValue INTEGER_VALUE_0 = new ParticularIntegerValue(0); + static final IntegerValue INTEGER_VALUE_1 = new ParticularIntegerValue(1); + static final IntegerValue INTEGER_VALUE_2 = new ParticularIntegerValue(2); + static final IntegerValue INTEGER_VALUE_3 = new ParticularIntegerValue(3); + static final IntegerValue INTEGER_VALUE_4 = new ParticularIntegerValue(4); + static final IntegerValue INTEGER_VALUE_5 = new ParticularIntegerValue(5); + static final LongValue LONG_VALUE_0 = new ParticularLongValue(0); + static final LongValue LONG_VALUE_1 = new ParticularLongValue(1); + static final FloatValue FLOAT_VALUE_0 = new ParticularFloatValue(0.0f); + static final FloatValue FLOAT_VALUE_1 = new ParticularFloatValue(1.0f); + static final FloatValue FLOAT_VALUE_2 = new ParticularFloatValue(2.0f); + static final DoubleValue DOUBLE_VALUE_0 = new ParticularDoubleValue(0.0); + static final DoubleValue DOUBLE_VALUE_1 = new ParticularDoubleValue(1.0); + + + private static int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f); + private static long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.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 ParticularIntegerValue(value); + } + } + + + public LongValue createLongValue(long value) + { + return value == 0 ? LONG_VALUE_0 : + value == 1 ? LONG_VALUE_1 : + new ParticularLongValue(value); + } + + + public FloatValue createFloatValue(float value) + { + // Make sure to distinguish between +0.0 and -0.0. + return value == 0.0f && Float.floatToIntBits(value) == POS_ZERO_FLOAT_BITS + ? FLOAT_VALUE_0 : + value == 1.0f ? FLOAT_VALUE_1 : + value == 2.0f ? FLOAT_VALUE_2 : + new ParticularFloatValue(value); + } + + + public DoubleValue createDoubleValue(double value) + { + // Make sure to distinguish between +0.0 and -0.0. + return value == 0.0 && Double.doubleToLongBits(value) == POS_ZERO_DOUBLE_BITS + ? DOUBLE_VALUE_0 : + value == 1.0 ? DOUBLE_VALUE_1 : + new ParticularDoubleValue(value); + } +} diff --git a/src/proguard/evaluation/value/TopValue.java b/src/proguard/evaluation/value/TopValue.java new file mode 100644 index 000000000..c7320fa28 --- /dev/null +++ b/src/proguard/evaluation/value/TopValue.java @@ -0,0 +1,79 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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 TopValue extends Category1Value +{ + // Implementations for Value. + + public boolean isSpecific() + { + return true; + } + + public boolean isParticular() + { + return true; + } + + public final Value generalize(Value other) + { + return this.getClass() == other.getClass() ? this : null; + } + + public final int computationalType() + { + return TYPE_TOP; + } + + public final String internalType() + { + return null; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return object != null && + this.getClass() == object.getClass(); + } + + + public int hashCode() + { + return this.getClass().hashCode(); + } + + + public String toString() + { + return "T"; + } +} diff --git a/src/proguard/evaluation/value/UnknownDoubleValue.java b/src/proguard/evaluation/value/UnknownDoubleValue.java new file mode 100644 index 000000000..f8bad9a60 --- /dev/null +++ b/src/proguard/evaluation/value/UnknownDoubleValue.java @@ -0,0 +1,125 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 represents a partially evaluated double value. + * + * @author Eric Lafortune + */ +public class UnknownDoubleValue extends DoubleValue +{ + // Basic unary methods. + + public DoubleValue negate() + { + return this; + } + + public IntegerValue convertToInteger() + { + return ValueFactory.INTEGER_VALUE; + } + + public LongValue convertToLong() + { + return ValueFactory.LONG_VALUE; + } + + public FloatValue convertToFloat() + { + return ValueFactory.FLOAT_VALUE; + } + + + // Basic binary methods. + + public DoubleValue generalize(DoubleValue other) + { + return this; + } + + public DoubleValue add(DoubleValue other) + { + return this; + } + + public DoubleValue subtract(DoubleValue other) + { + return this; + } + + public DoubleValue subtractFrom(DoubleValue other) + { + return this; + } + + public DoubleValue multiply(DoubleValue other) + { + return this; + } + + public DoubleValue divide(DoubleValue other) + { + return this; + } + + public DoubleValue divideOf(DoubleValue other) + { + return this; + } + + public DoubleValue remainder(DoubleValue other) + { + return this; + } + + public DoubleValue remainderOf(DoubleValue other) + { + return this; + } + + public IntegerValue compare(DoubleValue other) + { + return ValueFactory.INTEGER_VALUE; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return object != null && + this.getClass() == object.getClass(); + } + + + public int hashCode() + { + return this.getClass().hashCode(); + } + + + public String toString() + { + return "d"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/UnknownFloatValue.java b/src/proguard/evaluation/value/UnknownFloatValue.java new file mode 100644 index 000000000..464ceedeb --- /dev/null +++ b/src/proguard/evaluation/value/UnknownFloatValue.java @@ -0,0 +1,125 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 represents a partially evaluated float value. + * + * @author Eric Lafortune + */ +public class UnknownFloatValue extends FloatValue +{ + // Basic unary methods. + + public FloatValue negate() + { + return this; + } + + public IntegerValue convertToInteger() + { + return ValueFactory.INTEGER_VALUE; + } + + public LongValue convertToLong() + { + return ValueFactory.LONG_VALUE; + } + + public DoubleValue convertToDouble() + { + return ValueFactory.DOUBLE_VALUE; + } + + + // Basic binary methods. + + public FloatValue generalize(FloatValue other) + { + return this; + } + + public FloatValue add(FloatValue other) + { + return this; + } + + public FloatValue subtract(FloatValue other) + { + return this; + } + + public FloatValue subtractFrom(FloatValue other) + { + return this; + } + + public FloatValue multiply(FloatValue other) + { + return this; + } + + public FloatValue divide(FloatValue other) + { + return this; + } + + public FloatValue divideOf(FloatValue other) + { + return this; + } + + public FloatValue remainder(FloatValue other) + { + return this; + } + + public FloatValue remainderOf(FloatValue other) + { + return this; + } + + public IntegerValue compare(FloatValue other) + { + return ValueFactory.INTEGER_VALUE; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return object != null && + this.getClass() == object.getClass(); + } + + + public int hashCode() + { + return this.getClass().hashCode(); + } + + + public String toString() + { + return "f"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/UnknownIntegerValue.java b/src/proguard/evaluation/value/UnknownIntegerValue.java new file mode 100644 index 000000000..b273b12cb --- /dev/null +++ b/src/proguard/evaluation/value/UnknownIntegerValue.java @@ -0,0 +1,216 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 represents a partially evaluated integer value. + * + * @author Eric Lafortune + */ +public class UnknownIntegerValue extends IntegerValue +{ + // Basic unary methods. + + public IntegerValue negate() + { + return this; + } + + public IntegerValue convertToByte() + { + return this; + } + + public IntegerValue convertToCharacter() + { + return this; + } + + public IntegerValue convertToShort() + { + return this; + } + + public LongValue convertToLong() + { + return ValueFactory.LONG_VALUE; + } + + public FloatValue convertToFloat() + { + return ValueFactory.FLOAT_VALUE; + } + + public DoubleValue convertToDouble() + { + return ValueFactory.DOUBLE_VALUE; + } + + + // Basic binary methods. + + public IntegerValue generalize(IntegerValue other) + { + return this; + } + + + public IntegerValue add(IntegerValue other) + { + return this; + } + + public IntegerValue subtract(IntegerValue other) + { + return this; + } + + public IntegerValue subtractFrom(IntegerValue other) + { + return this; + } + + public IntegerValue multiply(IntegerValue other) + throws ArithmeticException + { + return this; + } + + public IntegerValue divide(IntegerValue other) + throws ArithmeticException + { + return this; + } + + public IntegerValue divideOf(IntegerValue other) + throws ArithmeticException + { + return this; + } + + public IntegerValue remainder(IntegerValue other) + throws ArithmeticException + { + return this; + } + + public IntegerValue remainderOf(IntegerValue other) + throws ArithmeticException + { + return this; + } + + public IntegerValue shiftLeft(IntegerValue other) + { + return this; + } + + public IntegerValue shiftLeftOf(IntegerValue other) + { + return this; + } + + public IntegerValue shiftRight(IntegerValue other) + { + return this; + } + + public IntegerValue shiftRightOf(IntegerValue other) + { + return this; + } + + public IntegerValue unsignedShiftRight(IntegerValue other) + { + return this; + } + + public IntegerValue unsignedShiftRightOf(IntegerValue other) + { + return this; + } + + public LongValue shiftLeftOf(LongValue other) + { + return ValueFactory.LONG_VALUE; + } + + public LongValue shiftRightOf(LongValue other) + { + return ValueFactory.LONG_VALUE; + } + + public LongValue unsignedShiftRightOf(LongValue other) + { + return ValueFactory.LONG_VALUE; + } + + public IntegerValue and(IntegerValue other) + { + return this; + } + + public IntegerValue or(IntegerValue other) + { + return this; + } + + public IntegerValue xor(IntegerValue other) + { + return this; + } + + public int equal(IntegerValue other) + { + return MAYBE; + } + + public int lessThan(IntegerValue other) + { + return MAYBE; + } + + public int lessThanOrEqual(IntegerValue other) + { + return MAYBE; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return object != null && + this.getClass() == object.getClass(); + } + + + public int hashCode() + { + return this.getClass().hashCode(); + } + + + public String toString() + { + return "i"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/UnknownLongValue.java b/src/proguard/evaluation/value/UnknownLongValue.java new file mode 100644 index 000000000..ee315bef4 --- /dev/null +++ b/src/proguard/evaluation/value/UnknownLongValue.java @@ -0,0 +1,160 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 represents a partially evaluated long value. + * + * @author Eric Lafortune + */ +public class UnknownLongValue extends LongValue +{ + // Basic unary methods. + + public LongValue negate() + { + return this; + } + + public IntegerValue convertToInteger() + { + return ValueFactory.INTEGER_VALUE; + } + + public FloatValue convertToFloat() + { + return ValueFactory.FLOAT_VALUE; + } + + public DoubleValue convertToDouble() + { + return ValueFactory.DOUBLE_VALUE; + } + + + // Basic binary methods. + + public LongValue generalize(LongValue other) + { + return this; + } + + public LongValue add(LongValue other) + { + return this; + } + + public LongValue subtract(LongValue other) + { + return this; + } + + public LongValue subtractFrom(LongValue other) + { + return this; + } + + public LongValue multiply(LongValue other) + throws ArithmeticException + { + return this; + } + + public LongValue divide(LongValue other) + throws ArithmeticException + { + return this; + } + + public LongValue divideOf(LongValue other) + throws ArithmeticException + { + return this; + } + + public LongValue remainder(LongValue other) + throws ArithmeticException + { + return this; + } + + public LongValue remainderOf(LongValue other) + throws ArithmeticException + { + return this; + } + + public LongValue shiftLeft(IntegerValue other) + { + return this; + } + + public LongValue shiftRight(IntegerValue other) + { + return this; + } + + public LongValue unsignedShiftRight(IntegerValue other) + { + return this; + } + + public LongValue and(LongValue other) + { + return this; + } + + public LongValue or(LongValue other) + { + return this; + } + + public LongValue xor(LongValue other) + { + return this; + } + + public IntegerValue compare(LongValue other) + { + return ValueFactory.INTEGER_VALUE; + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + return object != null && + this.getClass() == object.getClass(); + } + + + public int hashCode() + { + return this.getClass().hashCode(); + } + + + public String toString() + { + return "l"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/Value.java b/src/proguard/evaluation/value/Value.java new file mode 100644 index 000000000..f8d93272a --- /dev/null +++ b/src/proguard/evaluation/value/Value.java @@ -0,0 +1,169 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 abstract class represents a partially evaluated value. + * + * @author Eric Lafortune + */ +public abstract class Value +{ + public static final int NEVER = -1; + public static final int MAYBE = 0; + public static final int ALWAYS = 1; + + public static final int TYPE_INTEGER = 1; + public static final int TYPE_LONG = 2; + public static final int TYPE_FLOAT = 3; + 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; + + + /** + * Returns this Value as a Category1Value. + */ + public Category1Value category1Value() + { + throw new IllegalArgumentException("Value is not a Category 1 value [" + this.getClass().getName() + "]"); + } + + /** + * Returns this Value as a Category2Value. + */ + public Category2Value category2Value() + { + throw new IllegalArgumentException("Value is not a Category 2 value [" + this.getClass().getName() + "]"); + } + + + /** + * Returns this Value as an IntegerValue. + */ + public IntegerValue integerValue() + { + throw new IllegalArgumentException("Value is not an integer value [" + this.getClass().getName() + "]"); + } + + /** + * Returns this Value as a LongValue. + */ + public LongValue longValue() + { + throw new IllegalArgumentException("Value is not a long value [" + this.getClass().getName() + "]"); + } + + /** + * Returns this Value as a FloatValue. + */ + public FloatValue floatValue() + { + throw new IllegalArgumentException("Value is not a float value [" + this.getClass().getName() + "]"); + } + + /** + * Returns this Value as a DoubleValue. + */ + public DoubleValue doubleValue() + { + throw new IllegalArgumentException("Value is not a double value [" + this.getClass().getName() + "]"); + } + + /** + * Returns this Value as a ReferenceValue. + */ + public ReferenceValue referenceValue() + { + throw new IllegalArgumentException("Value is not a reference value [" + this.getClass().getName() + "]"); + } + + /** + * Returns this Value as an InstructionOffsetValue. + */ + public InstructionOffsetValue instructionOffsetValue() + { + throw new IllegalArgumentException("Value is not an instruction offset value [" + this.getClass().getName() + "]"); + } + + + /** + * Returns whether this Value represents a single specific (but possibly + * unknown) value. + */ + public boolean isSpecific() + { + return false; + } + + + /** + * Returns whether this Value represents a single particular (known) + * value. + */ + public boolean isParticular() + { + return false; + } + + + /** + * Returns the generalization of this Value and the given other Value. + */ + public abstract Value generalize(Value other); + + + /** + * Returns whether the computational type of this Value is a category 2 type. + * This means that it takes up the space of two category 1 types on the + * stack, for instance. + */ + public abstract boolean isCategory2(); + + + /** + * Returns the computational type of this Value. + * @return TYPE_INTEGER, + * TYPE_LONG, + * TYPE_FLOAT, + * TYPE_DOUBLE, + * TYPE_REFERENCE, or + * TYPE_INSTRUCTION_OFFSET. + */ + public abstract int computationalType(); + + + /** + * Returns the internal type of this Value. + * @return 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, + * ClassConstants.INTERNAL_TYPE_CLASS_START ... ClassConstants.INTERNAL_TYPE_CLASS_END, 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 000000000..c94ac6520 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 UnknownIntegerValue(); + static final LongValue LONG_VALUE = new UnknownLongValue(); + static final FloatValue FLOAT_VALUE = new UnknownFloatValue(); + static final DoubleValue DOUBLE_VALUE = new UnknownDoubleValue(); + + 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 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 createIntegerValue(); + case ClassConstants.INTERNAL_TYPE_LONG: return createLongValue(); + case ClassConstants.INTERNAL_TYPE_FLOAT: return createFloatValue(); + case ClassConstants.INTERNAL_TYPE_DOUBLE: return createDoubleValue(); + 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 particular 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 particular 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 particular 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 particular value. + */ + public DoubleValue createDoubleValue(double value) + { + return createDoubleValue(); + } + + + /** + * Creates a new ReferenceValue that represents null. + */ + 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 null, + * the ReferenceValue represents null. + */ + 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/evaluation/value/package.html b/src/proguard/evaluation/value/package.html new file mode 100644 index 000000000..71e1b1365 --- /dev/null +++ b/src/proguard/evaluation/value/package.html @@ -0,0 +1,3 @@ + +This package contains classes that represent partial evaluation values. + diff --git a/src/proguard/gradle/ProGuardTask.java b/src/proguard/gradle/ProGuardTask.java new file mode 100644 index 000000000..4aa168f5a --- /dev/null +++ b/src/proguard/gradle/ProGuardTask.java @@ -0,0 +1,1416 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.gradle; + +import groovy.lang.Closure; +import org.gradle.api.*; +import org.gradle.api.file.*; +import org.gradle.api.tasks.TaskAction; +import proguard.*; +import proguard.classfile.*; +import proguard.classfile.util.ClassUtil; +import proguard.util.ListUtil; + +import java.io.*; +import java.util.*; + +/** + * This Task allows to configure and run ProGuard from Gradle. + * + * @author Eric Lafortune + */ +public class ProGuardTask extends DefaultTask +{ + private final Configuration configuration = new Configuration(); + + // Field acting as a parameter for the class member specification methods. + private ClassSpecification classSpecification; + + + // Gradle task settings. + + public void configuration(Object configurationFile) + throws ParseException, IOException + { + ConfigurationParser parser = + new ConfigurationParser(resolvedFile(configurationFile), + System.getProperties()); + try + { + parser.parse(configuration); + } + finally + { + parser.close(); + } + } + + public void injars(Object programJars) + throws ParseException + { + injars(null, programJars); + } + + public void injars(Map filterArgs, Object programJars) + throws ParseException + { + configuration.programJars = + extendClassPath(configuration.programJars, + resolvedFiles(programJars), + filterArgs, + false); + } + + public void outjars(Object programJars) + throws ParseException + { + outjars(null, programJars); + } + + public void outjars(Map filterArgs, Object programJars) + throws ParseException + { + configuration.programJars = + extendClassPath(configuration.programJars, + resolvedFiles(programJars), + filterArgs, + true); + } + + public void libraryjars(Object libraryJars) + throws ParseException + { + libraryjars(null, libraryJars); + } + + public void libraryjars(Map filterArgs, Object libraryJars) + throws ParseException + { + configuration.libraryJars = + extendClassPath(configuration.libraryJars, + resolvedFiles(libraryJars), + filterArgs, + false); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getskipnonpubliclibraryclasses() + { + skipnonpubliclibraryclasses(); + return null; + } + + public void skipnonpubliclibraryclasses() + { + configuration.skipNonPublicLibraryClasses = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getdontskipnonpubliclibraryclassmembers() + { + dontskipnonpubliclibraryclassmembers(); + return null; + } + + public void dontskipnonpubliclibraryclassmembers() + { + configuration.skipNonPublicLibraryClassMembers = false; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getkeepdirectories() + { + keepdirectories(); + return null; + } + + public void keepdirectories() + { + keepdirectories(null); + } + + public void keepdirectories(String filter) + { + configuration.keepDirectories = + extendFilter(configuration.keepDirectories, filter); + } + + public void target(String targetClassVersion) + { + configuration.targetClassVersion = + ClassUtil.internalClassVersion(targetClassVersion); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getforceprocessing() + { + forceprocessing(); + return null; + } + + public void forceprocessing() + { + configuration.lastModified = Long.MAX_VALUE; + } + + public void keep(String classSpecificationString) + throws ParseException + { + keep(null, classSpecificationString); + } + + public void keep(Map keepArgs, + String classSpecificationString) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(false, + true, + false, + keepArgs, + classSpecificationString)); + } + + public void keep(Map keepClassSpecificationArgs) + throws ParseException + { + keep(keepClassSpecificationArgs, (Closure)null); + } + + public void keep(Map keepClassSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(false, + true, + false, + keepClassSpecificationArgs, + classMembersClosure)); + } + + public void keepclassmembers(String classSpecificationString) + throws ParseException + { + keepclassmembers(null, classSpecificationString); + } + + public void keepclassmembers(Map keepArgs, + String classSpecificationString) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(false, + false, + false, + keepArgs, + classSpecificationString)); + } + + public void keepclassmembers(Map keepClassSpecificationArgs) + throws ParseException + { + keepclassmembers(keepClassSpecificationArgs, (Closure)null); + } + + public void keepclassmembers(Map keepClassSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(false, + false, + false, + keepClassSpecificationArgs, + classMembersClosure)); + } + + public void keepclasseswithmembers(String classSpecificationString) + throws ParseException + { + keepclasseswithmembers(null, classSpecificationString); + } + + public void keepclasseswithmembers(Map keepArgs, + String classSpecificationString) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(false, + false, + true, + keepArgs, + classSpecificationString)); + } + + public void keepclasseswithmembers(Map keepClassSpecificationArgs) + throws ParseException + { + keepclasseswithmembers(keepClassSpecificationArgs, (Closure)null); + } + + public void keepclasseswithmembers(Map keepClassSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(false, + false, + true, + keepClassSpecificationArgs, + classMembersClosure)); + } + + public void keepnames(String classSpecificationString) + throws ParseException + { + keepnames(null, classSpecificationString); + } + + public void keepnames(Map keepArgs, + String classSpecificationString) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(true, + true, + false, + keepArgs, + classSpecificationString)); + } + + public void keepnames(Map keepClassSpecificationArgs) + throws ParseException + { + keepnames(keepClassSpecificationArgs, (Closure)null); + } + + public void keepnames(Map keepClassSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(true, + true, + false, + keepClassSpecificationArgs, + classMembersClosure)); + } + + public void keepclassmembernames(String classSpecificationString) + throws ParseException + { + keepclassmembernames(null, classSpecificationString); + } + + public void keepclassmembernames(Map keepArgs, + String classSpecificationString) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(true, + false, + false, + keepArgs, + classSpecificationString)); + } + + public void keepclassmembernames(Map keepClassSpecificationArgs) + throws ParseException + { + keepclassmembernames(keepClassSpecificationArgs, (Closure)null); + } + + public void keepclassmembernames(Map keepClassSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(true, + false, + false, + keepClassSpecificationArgs, + classMembersClosure)); + } + + public void keepclasseswithmembernames(String classSpecificationString) + throws ParseException + { + keepclasseswithmembernames(null, classSpecificationString); + } + + public void keepclasseswithmembernames(Map keepArgs, + String classSpecificationString) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(true, + false, + true, + keepArgs, + classSpecificationString)); + } + + public void keepclasseswithmembernames(Map keepClassSpecificationArgs) + throws ParseException + { + keepclasseswithmembernames(keepClassSpecificationArgs, (Closure)null); + } + + public void keepclasseswithmembernames(Map keepClassSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + configuration.keep = + extendClassSpecifications(configuration.keep, + createKeepClassSpecification(true, + false, + true, + keepClassSpecificationArgs, + classMembersClosure)); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getprintseeds() + { + printseeds(); + return null; + } + + public void printseeds() + { + configuration.printSeeds = Configuration.STD_OUT; + } + + public void printseeds(Object printSeeds) + throws ParseException + { + configuration.printSeeds = resolvedFile(printSeeds); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getdontshrink() + { + dontshrink(); + return null; + } + + public void dontshrink() + { + configuration.shrink = false; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getprintusage() + { + printusage(); + return null; + } + + public void printusage() + { + configuration.printUsage = Configuration.STD_OUT; + } + + public void printusage(Object printUsage) + throws ParseException + { + configuration.printUsage = resolvedFile(printUsage); + } + + public void whyareyoukeeping(String classSpecificationString) + throws ParseException + { + configuration.whyAreYouKeeping = + extendClassSpecifications(configuration.whyAreYouKeeping, + createClassSpecification(classSpecificationString)); + } + + public void whyareyoukeeping(Map classSpecificationArgs) + throws ParseException + { + whyareyoukeeping(classSpecificationArgs, null); + } + + public void whyareyoukeeping(Map classSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + configuration.whyAreYouKeeping = + extendClassSpecifications(configuration.whyAreYouKeeping, + createClassSpecification(classSpecificationArgs, + classMembersClosure)); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getdontoptimize() + { + dontoptimize(); + return null; + } + + public void dontoptimize() + { + configuration.optimize = false; + } + + public void optimizations(String filter) + { + configuration.optimizations = + extendFilter(configuration.optimizations, filter); + } + + + public void optimizationpasses(int optimizationPasses) + { + configuration.optimizationPasses = optimizationPasses; + } + + public void assumenosideeffects(String classSpecificationString) + throws ParseException + { + configuration.assumeNoSideEffects = + extendClassSpecifications(configuration.assumeNoSideEffects, + createClassSpecification(classSpecificationString)); + } + + public void assumenosideeffects(Map classSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + configuration.assumeNoSideEffects = + extendClassSpecifications(configuration.assumeNoSideEffects, + createClassSpecification(classSpecificationArgs, + classMembersClosure)); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getallowaccessmodification() + { + allowaccessmodification(); + return null; + } + + public void allowaccessmodification() + { + configuration.allowAccessModification = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getmergeinterfacesaggressively() + { + mergeinterfacesaggressively(); + return null; + } + + public void mergeinterfacesaggressively() + { + configuration.mergeInterfacesAggressively = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getdontobfuscate() + { + dontobfuscate(); + return null; + } + + public void dontobfuscate() + { + configuration.obfuscate = false; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getprintmapping() + { + printmapping(); + return null; + } + + public void printmapping() + { + configuration.printMapping = Configuration.STD_OUT; + } + + public void printmapping(Object printMapping) + throws ParseException + { + configuration.printMapping = resolvedFile(printMapping); + } + + public void applymapping(Object applyMapping) + throws ParseException + { + configuration.applyMapping = resolvedFile(applyMapping); + } + + public void obfuscationdictionary(Object obfuscationDictionary) + throws ParseException + { + configuration.obfuscationDictionary = + resolvedFile(obfuscationDictionary); + } + + public void classobfuscationdictionary(Object classObfuscationDictionary) + throws ParseException + { + configuration.classObfuscationDictionary = + resolvedFile(classObfuscationDictionary); + } + + public void packageobfuscationdictionary(Object packageObfuscationDictionary) + throws ParseException + { + configuration.packageObfuscationDictionary = + resolvedFile(packageObfuscationDictionary); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getoverloadaggressively() + { + overloadaggressively(); + return null; + } + + public void overloadaggressively() + { + configuration.overloadAggressively = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getuseuniqueclassmembernames() + { + useuniqueclassmembernames(); + return null; + } + + public void useuniqueclassmembernames() + { + configuration.useUniqueClassMemberNames = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getdontusemixedcaseclassnames() + { + dontusemixedcaseclassnames(); + return null; + } + + public void dontusemixedcaseclassnames() + { + configuration.useMixedCaseClassNames = false; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getkeeppackagenames() + { + keeppackagenames(); + return null; + } + + public void keeppackagenames() + { + keeppackagenames(null); + } + + public void keeppackagenames(String filter) + { + configuration.keepPackageNames = + extendFilter(configuration.keepPackageNames, filter, true); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getflattenpackagehierarchy() + { + flattenpackagehierarchy(); + return null; + } + + public void flattenpackagehierarchy() + { + flattenpackagehierarchy(""); + } + + public void flattenpackagehierarchy(String flattenPackageHierarchy) + { + configuration.flattenPackageHierarchy = + ClassUtil.internalClassName(flattenPackageHierarchy); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getrepackageclasses() + { + repackageclasses(); + return null; + } + + public void repackageclasses() + { + repackageclasses(""); + } + + public void repackageclasses(String repackageClasses) + { + configuration.repackageClasses = + ClassUtil.internalClassName(repackageClasses); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getkeepattributes() + { + keepattributes(); + return null; + } + + public void keepattributes() + { + keepattributes(null); + } + + public void keepattributes(String filter) + { + configuration.keepAttributes = + extendFilter(configuration.keepAttributes, filter); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getkeepparameternames() + { + keepparameternames(); + return null; + } + + public void keepparameternames() + { + configuration.keepParameterNames = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getrenamesourcefileattribute() + { + renamesourcefileattribute(); + return null; + } + + public void renamesourcefileattribute() + { + renamesourcefileattribute(""); + } + + public void renamesourcefileattribute(String newSourceFileAttribute) + { + configuration.newSourceFileAttribute = newSourceFileAttribute; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getadaptclassstrings() + { + adaptclassstrings(); + return null; + } + + public void adaptclassstrings() + { + adaptclassstrings(null); + } + + public void adaptclassstrings(String filter) + { + configuration.adaptClassStrings = + extendFilter(configuration.adaptClassStrings, filter, true); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getadaptresourcefilenames() + { + adaptresourcefilenames(); + return null; + } + + public void adaptresourcefilenames() + { + adaptresourcefilenames(null); + } + + public void adaptresourcefilenames(String filter) + { + configuration.adaptResourceFileNames = + extendFilter(configuration.adaptResourceFileNames, filter); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getadaptresourcefilecontents() + { + adaptresourcefilecontents(); + return null; + } + + public void adaptresourcefilecontents() + { + adaptresourcefilecontents(null); + } + + public void adaptresourcefilecontents(String filter) + { + configuration.adaptResourceFileContents = + extendFilter(configuration.adaptResourceFileContents, filter); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getdontpreverify() + { + dontpreverify(); + return null; + } + + public void dontpreverify() + { + configuration.preverify = false; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getmicroedition() + { + microedition(); + return null; + } + + public void microedition() + { + configuration.microEdition = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getverbose() + { + verbose(); + return null; + } + + public void verbose() + { + configuration.verbose = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getdontnote() + { + dontnote(); + return null; + } + + public void dontnote() + { + dontnote(null); + } + + public void dontnote(String filter) + { + configuration.note = extendFilter(configuration.note, filter, true); + } + + + // Hack: support the keyword without parentheses in Groovy. + public Object getdontwarn() + { + dontwarn(); + return null; + } + + public void dontwarn() + { + dontwarn(null); + } + + public void dontwarn(String filter) + { + configuration.warn = extendFilter(configuration.warn, filter, true); + } + + + // Hack: support the keyword without parentheses in Groovy. + public Object getignorewarnings() + { + ignorewarnings(); + return null; + } + + public void ignorewarnings() + { + configuration.ignoreWarnings = true; + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getprintconfiguration() + { + printconfiguration(); + return null; + } + + public void printconfiguration() + { + configuration.printConfiguration = Configuration.STD_OUT; + } + + public void printconfiguration(Object printConfiguration) + throws ParseException + { + configuration.printConfiguration = resolvedFile(printConfiguration); + } + + // Hack: support the keyword without parentheses in Groovy. + public Object getdump() + { + dump(); + return null; + } + + public void dump() + { + configuration.dump = Configuration.STD_OUT; + } + + public void dump(Object dump) + throws ParseException + { + configuration.dump = resolvedFile(dump); + } + + + // Class member methods. + + public void field(Map memberSpecificationArgs) + throws ParseException + { + if (classSpecification == null) + { + throw new IllegalArgumentException("The 'field' method can only be used nested inside a class specification."); + } + + classSpecification.addField(createMemberSpecification(false, + false, + memberSpecificationArgs)); + } + + + public void constructor(Map memberSpecificationArgs) + throws ParseException + { + if (classSpecification == null) + { + throw new IllegalArgumentException("The 'constructor' method can only be used nested inside a class specification."); + } + + classSpecification.addMethod(createMemberSpecification(true, + true, + memberSpecificationArgs)); + } + + + public void method(Map memberSpecificationArgs) + throws ParseException + { + if (classSpecification == null) + { + throw new IllegalArgumentException("The 'method' method can only be used nested inside a class specification."); + } + + classSpecification.addMethod(createMemberSpecification(true, + false, + memberSpecificationArgs)); + } + + + // Gradle task execution. + + @TaskAction + public void proguard() throws IOException + { + new ProGuard(configuration).execute(); + } + + + // Small utility methods. + + /** + * Returns a file that is properly resolved with respect to the project + * directory. + */ + private File resolvedFile(Object file) + throws ParseException + { + return getProject().file(resolvedString(file)); + } + + + /** + * Returns a file collection that is properly resolved with respect to the + * project directory. + */ + private ConfigurableFileCollection resolvedFiles(Object files) + throws ParseException + { + return getProject().files(new Object[] { resolvedString(files) }); + } + + + /** + * Returns the given Object, with resolved properties if it is a String. + */ + private Object resolvedString(Object object) + throws ParseException + { + if (object instanceof String) + { + try + { + String fileName = (String)object; + return + new ConfigurationParser(fileName, + "Gradle setting", + null, + System.getProperties()) + .replaceSystemProperties(fileName); + } + catch (IOException e) + { + throw new ParseException(e.getMessage()); + } + } + + return object; + } + + + private ClassPath extendClassPath(ClassPath classPath, + ConfigurableFileCollection fileCollection, + Map filterArgs, + boolean output) + { + if (classPath == null) + { + classPath = new ClassPath(); + } + + Iterator files = fileCollection.iterator(); + while (files.hasNext()) + { + File file = (File)files.next(); + + // Create the class path entry. + ClassPathEntry classPathEntry = new ClassPathEntry(file, output); + + // Add any filters to the class path entry. + if (filterArgs != null) + { + classPathEntry.setFilter(ListUtil.commaSeparatedList((String)filterArgs.get("filter"))); + classPathEntry.setJarFilter(ListUtil.commaSeparatedList((String)filterArgs.get("jarfilter"))); + classPathEntry.setWarFilter(ListUtil.commaSeparatedList((String)filterArgs.get("warfilter"))); + classPathEntry.setEarFilter(ListUtil.commaSeparatedList((String)filterArgs.get("earfilter"))); + classPathEntry.setZipFilter(ListUtil.commaSeparatedList((String)filterArgs.get("zipfilter"))); + } + + classPath.add(classPathEntry); + } + + return classPath; + } + + + private KeepClassSpecification createKeepClassSpecification(boolean allowShrinking, + boolean markClasses, + boolean markConditionally, + Map keepArgs, + String classSpecificationString) + throws ParseException + { + ClassSpecification classSpecification = + createClassSpecification(classSpecificationString); + + return + createKeepClassSpecification(allowShrinking, + markClasses, + markConditionally, + keepArgs, + classSpecification); + } + + + private KeepClassSpecification createKeepClassSpecification(boolean allowShrinking, + boolean markClasses, + boolean markConditionally, + Map classSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + ClassSpecification classSpecification = + createClassSpecification(classSpecificationArgs, + classMembersClosure); + return + createKeepClassSpecification(allowShrinking, + markClasses, + markConditionally, + classSpecificationArgs, + classSpecification); + } + + + private KeepClassSpecification createKeepClassSpecification(boolean allowShrinking, + boolean markClasses, + boolean markConditionally, + Map keepArgs, + ClassSpecification classSpecification) + { + return + new KeepClassSpecification(markClasses, + markConditionally, + retrieveBoolean(keepArgs, "allowshrinking", allowShrinking), + retrieveBoolean(keepArgs, "allowoptimization", false), + retrieveBoolean(keepArgs, "allowobfuscation", false), + classSpecification); + } + + + private ClassSpecification createClassSpecification(String classSpecificationString) + throws ParseException + { + try + { + ConfigurationParser parser = + new ConfigurationParser(new String[] { classSpecificationString }, null); + + try + { + return parser.parseClassSpecificationArguments(); + } + finally + { + parser.close(); + } + } + catch (IOException e) + { + throw new ParseException(e.getMessage()); + } + } + + + private ClassSpecification createClassSpecification(Map classSpecificationArgs, + Closure classMembersClosure) + throws ParseException + { + // Extract the arguments. + String access = (String)classSpecificationArgs.get("access"); + String annotation = (String)classSpecificationArgs.get("annotation"); + String type = (String)classSpecificationArgs.get("type"); + String name = (String)classSpecificationArgs.get("name"); + String extendsAnnotation = (String)classSpecificationArgs.get("extendsannotation"); + String extends_ = (String)classSpecificationArgs.get("extends"); + if (extends_ == null) + { + extends_ = (String)classSpecificationArgs.get("implements"); + } + + // Create the class specification. + ClassSpecification classSpecification = + new ClassSpecification(null, + requiredClassAccessFlags(true, access, type), + requiredClassAccessFlags(false, access, type), + annotation != null ? ClassUtil.internalType(annotation) : null, + name != null ? ClassUtil.internalClassName(name) : null, + extendsAnnotation != null ? ClassUtil.internalType(extendsAnnotation) : null, + extends_ != null ? ClassUtil.internalClassName(extends_) : null); + + // Initialize the class specification with its closure. + if (classMembersClosure != null) + { + // Temporarily remember the class specification, so we can add + // class member specifications. + this.classSpecification = classSpecification; + classMembersClosure.call(classSpecification); + this.classSpecification = null; + } + + return classSpecification; + } + + + private int requiredClassAccessFlags(boolean set, + String access, + String type) + throws ParseException + { + int accessFlags = 0; + + if (access != null) + { + StringTokenizer tokenizer = new StringTokenizer(access, " ,"); + while (tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken(); + + if (token.startsWith("!") ^ set) + { + String strippedToken = token.startsWith("!") ? + token.substring(1) : + token; + + int accessFlag = + strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC) ? ClassConstants.INTERNAL_ACC_PUBLIC : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC) ? ClassConstants.INTERNAL_ACC_SYNTHETIC : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_ANNOTATION) ? ClassConstants.INTERNAL_ACC_ANNOTATTION : + 0; + + if (accessFlag == 0) + { + throw new ParseException("Incorrect class access modifier ["+strippedToken+"]"); + } + + accessFlags |= accessFlag; + } + } + } + + if (type != null && (type.startsWith("!") ^ set)) + { + int accessFlag = + type.equals("class") ? 0 : + type.equals( ClassConstants.EXTERNAL_ACC_INTERFACE) || + type.equals("!" + ClassConstants.EXTERNAL_ACC_INTERFACE) ? ClassConstants.INTERNAL_ACC_INTERFACE : + type.equals( ClassConstants.EXTERNAL_ACC_ENUM) || + type.equals("!" + ClassConstants.EXTERNAL_ACC_ENUM) ? ClassConstants.INTERNAL_ACC_ENUM : + -1; + if (accessFlag == -1) + { + throw new ParseException("Incorrect class type ["+type+"]"); + } + + accessFlags |= accessFlag; + } + + return accessFlags; + } + + + private MemberSpecification createMemberSpecification(boolean isMethod, + boolean isConstructor, + Map classSpecificationArgs) + throws ParseException + { + // Extract the arguments. + String access = (String)classSpecificationArgs.get("access"); + String type = (String)classSpecificationArgs.get("type"); + String annotation = (String)classSpecificationArgs.get("annotation"); + String name = (String)classSpecificationArgs.get("name"); + String parameters = (String)classSpecificationArgs.get("parameters"); + + // Perform some basic conversions and checks on the attributes. + if (annotation != null) + { + annotation = ClassUtil.internalType(annotation); + } + + if (isMethod) + { + if (isConstructor) + { + if (type != null) + { + throw new ParseException("Type attribute not allowed in constructor specification ["+type+"]"); + } + + if (parameters != null) + { + type = ClassConstants.EXTERNAL_TYPE_VOID; + } + + name = ClassConstants.INTERNAL_METHOD_NAME_INIT; + } + else if ((type != null) ^ (parameters != null)) + { + throw new ParseException("Type and parameters attributes must always be present in combination in method specification"); + } + } + else + { + if (parameters != null) + { + throw new ParseException("Parameters attribute not allowed in field specification ["+parameters+"]"); + } + } + + List parameterList = ListUtil.commaSeparatedList(parameters); + + String descriptor = + parameters != null ? ClassUtil.internalMethodDescriptor(type, parameterList) : + type != null ? ClassUtil.internalType(type) : + null; + + return new MemberSpecification(requiredMemberAccessFlags(true, access), + requiredMemberAccessFlags(false, access), + annotation, + name, + descriptor); + } + + + private int requiredMemberAccessFlags(boolean set, + String access) + throws ParseException + { + int accessFlags = 0; + + if (access != null) + { + StringTokenizer tokenizer = new StringTokenizer(access, " ,"); + while (tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken(); + + if (token.startsWith("!") ^ set) + { + String strippedToken = token.startsWith("!") ? + token.substring(1) : + token; + + int accessFlag = + strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC) ? ClassConstants.INTERNAL_ACC_PUBLIC : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_PRIVATE) ? ClassConstants.INTERNAL_ACC_PRIVATE : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_PROTECTED) ? ClassConstants.INTERNAL_ACC_PROTECTED : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_STATIC) ? ClassConstants.INTERNAL_ACC_STATIC : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED) ? ClassConstants.INTERNAL_ACC_SYNCHRONIZED : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_VOLATILE) ? ClassConstants.INTERNAL_ACC_VOLATILE : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_TRANSIENT) ? ClassConstants.INTERNAL_ACC_TRANSIENT : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_BRIDGE) ? ClassConstants.INTERNAL_ACC_BRIDGE : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_VARARGS) ? ClassConstants.INTERNAL_ACC_VARARGS : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_NATIVE) ? ClassConstants.INTERNAL_ACC_NATIVE : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_STRICT) ? ClassConstants.INTERNAL_ACC_STRICT : + strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC) ? ClassConstants.INTERNAL_ACC_SYNTHETIC : + 0; + + if (accessFlag == 0) + { + throw new ParseException("Incorrect class member access modifier ["+strippedToken+"]"); + } + + accessFlags |= accessFlag; + } + } + } + + return accessFlags; + } + + + private boolean retrieveBoolean(Map args, String name, boolean defaultValue) + { + if (args == null) + { + return defaultValue; + } + + Object arg = args.get(name); + + return arg == null ? defaultValue : ((Boolean)arg).booleanValue(); + } + + + private List extendClassSpecifications(List classSpecifications, + ClassSpecification classSpecification) + { + if (classSpecifications == null) + { + classSpecifications = new ArrayList(); + } + + classSpecifications.add(classSpecification); + + return classSpecifications; + } + + + private List extendClassSpecifications(List classSpecifications, + List additionalClassSpecifications) + { + if (additionalClassSpecifications != null) + { + if (classSpecifications == null) + { + classSpecifications = new ArrayList(); + } + + classSpecifications.addAll(additionalClassSpecifications); + } + + return classSpecifications; + } + + + private List extendFilter(List filter, + String filterString) + { + return extendFilter(filter, filterString, false); + } + + + private List extendFilter(List filter, + String filterString, + boolean internal) + { + if (filter == null) + { + filter = new ArrayList(); + } + + if (filterString == null) + { + // Clear the filter to keep all names. + filter.clear(); + } + else + { + if (internal) + { + filterString = ClassUtil.internalClassName(filterString); + } + + // Append the filter. + filter.addAll(ListUtil.commaSeparatedList(filterString)); + } + + return filter; + } +} diff --git a/src/proguard/gui/ClassPathPanel.java b/src/proguard/gui/ClassPathPanel.java new file mode 100644 index 000000000..8f41db69b --- /dev/null +++ b/src/proguard/gui/ClassPathPanel.java @@ -0,0 +1,441 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 proguard.util.ListUtil; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.util.List; + +/** + * This ListPanel allows the user to add, edit, filter, move, and + * remove ClassPathEntry objects in a ClassPath object. + * + * @author Eric Lafortune + */ +class ClassPathPanel extends ListPanel +{ + private final JFrame owner; + private final boolean inputAndOutput; + private final JFileChooser chooser; + private final FilterDialog filterDialog; + + + public ClassPathPanel(JFrame owner, boolean inputAndOutput) + { + super(); + + super.firstSelectionButton = inputAndOutput ? 3 : 2; + + this.owner = owner; + this.inputAndOutput = inputAndOutput; + + list.setCellRenderer(new MyListCellRenderer()); + + chooser = new JFileChooser(""); + chooser.setMultiSelectionEnabled(true); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.addChoosableFileFilter( + new ExtensionFileFilter(msg("jarWarEarZipExtensions"), + new String[] { ".jar", ".war", ".ear", ".zip" })); + chooser.setApproveButtonText(msg("ok")); + + filterDialog = new FilterDialog(owner, msg("enterFilter")); + + addAddButton(inputAndOutput, false); + if (inputAndOutput) + { + addAddButton(inputAndOutput, true); + } + addEditButton(); + addFilterButton(); + addRemoveButton(); + addUpButton(); + addDownButton(); + + enableSelectionButtons(); + } + + + protected void addAddButton(boolean inputAndOutput, + final boolean isOutput) + { + JButton addButton = new JButton(msg(inputAndOutput ? + isOutput ? "addOutput" : + "addInput" : + "add")); + addButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + chooser.setDialogTitle(msg("addJars")); + chooser.setSelectedFile(null); + chooser.setSelectedFiles(null); + + int returnValue = chooser.showOpenDialog(owner); + if (returnValue == JFileChooser.APPROVE_OPTION) + { + File[] selectedFiles = chooser.getSelectedFiles(); + ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput); + + // Add the new elements. + addElements(entries); + } + } + }); + + addButton(tip(addButton, inputAndOutput ? + isOutput ? "addOutputTip" : + "addInputTip" : + "addTip")); + } + + + protected void addEditButton() + { + JButton editButton = new JButton(msg("edit")); + editButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + boolean isOutput = false; + + int[] selectedIndices = list.getSelectedIndices(); + + // Copy the Object array into a File array. + File[] selectedFiles = new File[selectedIndices.length]; + for (int index = 0; index < selectedFiles.length; index++) + { + ClassPathEntry entry = + (ClassPathEntry)listModel.getElementAt(selectedIndices[index]); + + isOutput = entry.isOutput(); + + selectedFiles[index] = entry.getFile(); + } + + 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 + // the current directory. + chooser.setSelectedFile(selectedFiles[0].getAbsoluteFile()); + chooser.setSelectedFiles(selectedFiles); + + int returnValue = chooser.showOpenDialog(owner); + if (returnValue == JFileChooser.APPROVE_OPTION) + { + selectedFiles = chooser.getSelectedFiles(); + ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput); + + // If there are the same number of files selected now as + // there were before, we can just replace the old ones. + if (selectedIndices.length == selectedFiles.length) + { + // Replace the old elements. + setElementsAt(entries, selectedIndices); + } + else + { + // Remove the old elements. + removeElementsAt(selectedIndices); + + // Add the new elements. + addElements(entries); + } + } + } + }); + + addButton(tip(editButton, "editTip")); + } + + + protected void addFilterButton() + { + JButton filterButton = new JButton(msg("filter")); + filterButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if (!list.isSelectionEmpty()) + { + int[] selectedIndices = list.getSelectedIndices(); + + // Put the filters of the first selected entry in the dialog. + getFiltersFrom(selectedIndices[0]); + + int returnValue = filterDialog.showDialog(); + if (returnValue == FilterDialog.APPROVE_OPTION) + { + // Apply the entered filters to all selected entries. + setFiltersAt(selectedIndices); + } + } + } + }); + + addButton(tip(filterButton, "filterTip")); + } + + + /** + * Sets the ClassPath to be represented in this panel. + */ + public void setClassPath(ClassPath classPath) + { + listModel.clear(); + + if (classPath != null) + { + for (int index = 0; index < classPath.size(); index++) + { + listModel.addElement(classPath.get(index)); + } + } + + // Make sure the selection buttons are properly enabled, + // since the clear method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + /** + * Returns the ClassPath currently represented in this panel. + */ + public ClassPath getClassPath() + { + int size = listModel.size(); + if (size == 0) + { + return null; + } + + ClassPath classPath = new ClassPath(); + for (int index = 0; index < size; index++) + { + classPath.add((ClassPathEntry)listModel.get(index)); + } + + return classPath; + } + + + /** + * Converts the given array of File objects into a corresponding array of + * ClassPathEntry objects. + */ + private ClassPathEntry[] classPathEntries(File[] files, boolean isOutput) + { + ClassPathEntry[] entries = new ClassPathEntry[files.length]; + for (int index = 0; index < entries.length; index++) + { + entries[index] = new ClassPathEntry(files[index], isOutput); + } + return entries; + } + + + /** + * Sets up the filter dialog with the filters from the specified class path + * entry. + */ + private void getFiltersFrom(int index) + { + ClassPathEntry firstEntry = (ClassPathEntry)listModel.get(index); + + filterDialog.setFilter(firstEntry.getFilter()); + filterDialog.setJarFilter(firstEntry.getJarFilter()); + filterDialog.setWarFilter(firstEntry.getWarFilter()); + filterDialog.setEarFilter(firstEntry.getEarFilter()); + filterDialog.setZipFilter(firstEntry.getZipFilter()); + } + + + /** + * Applies the entered filter to the specified class path entries. + * Any previously set filters are discarded. + */ + private void setFiltersAt(int[] indices) + { + for (int index = indices.length - 1; index >= 0; index--) + { + ClassPathEntry entry = (ClassPathEntry)listModel.get(indices[index]); + entry.setFilter(filterDialog.getFilter()); + entry.setJarFilter(filterDialog.getJarFilter()); + entry.setWarFilter(filterDialog.getWarFilter()); + entry.setEarFilter(filterDialog.getEarFilter()); + entry.setZipFilter(filterDialog.getZipFilter()); + } + + // Make sure they are selected and thus repainted. + list.setSelectedIndices(indices); + } + + + /** + * 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 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 final Icon arrowIcon; + + + public MyListCellRenderer() + { + GridBagConstraints jarNameLabelConstraints = new GridBagConstraints(); + jarNameLabelConstraints.anchor = GridBagConstraints.WEST; + jarNameLabelConstraints.insets = new Insets(1, 2, 1, 2); + + GridBagConstraints filterLabelConstraints = new GridBagConstraints(); + filterLabelConstraints.gridwidth = GridBagConstraints.REMAINDER; + filterLabelConstraints.fill = GridBagConstraints.HORIZONTAL; + filterLabelConstraints.weightx = 1.0; + filterLabelConstraints.anchor = GridBagConstraints.EAST; + filterLabelConstraints.insets = jarNameLabelConstraints.insets; + + arrowIcon = new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(ARROW_IMAGE_FILE))); + + cellPanel.add(iconLabel, jarNameLabelConstraints); + cellPanel.add(jarNameLabel, jarNameLabelConstraints); + cellPanel.add(filterLabel, filterLabelConstraints); + } + + + // Implementations for ListCellRenderer. + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + ClassPathEntry entry = (ClassPathEntry)value; + + // Prepend an arrow to the output entries. + if (inputAndOutput && entry.isOutput()) + { + iconLabel.setIcon(arrowIcon); + } + else + { + iconLabel.setIcon(null); + } + + // Set the entry name text. + jarNameLabel.setText(entry.getName()); + + // Set the filter text. + StringBuffer filter = null; + filter = appendFilter(filter, entry.getZipFilter()); + filter = appendFilter(filter, entry.getEarFilter()); + filter = appendFilter(filter, entry.getWarFilter()); + filter = appendFilter(filter, entry.getJarFilter()); + filter = appendFilter(filter, entry.getFilter()); + + if (filter != null) + { + filter.append(')'); + } + + filterLabel.setText(filter != null ? filter.toString() : ""); + + // Set the colors. + if (isSelected) + { + cellPanel.setBackground(list.getSelectionBackground()); + jarNameLabel.setForeground(list.getSelectionForeground()); + filterLabel.setForeground(list.getSelectionForeground()); + } + else + { + cellPanel.setBackground(list.getBackground()); + jarNameLabel.setForeground(list.getForeground()); + filterLabel.setForeground(list.getForeground()); + } + + // Make the font color red if this is an input file that can't be read. + if (!(inputAndOutput && entry.isOutput()) && + !entry.getFile().canRead()) + { + jarNameLabel.setForeground(Color.red); + } + + cellPanel.setOpaque(true); + + return cellPanel; + } + + + private StringBuffer appendFilter(StringBuffer filter, List additionalFilter) + { + if (filter != null) + { + filter.append(';'); + } + + if (additionalFilter != null) + { + if (filter == null) + { + filter = new StringBuffer().append('('); + } + + filter.append(ListUtil.commaSeparatedString(additionalFilter, true)); + } + + return filter; + } + } +} diff --git a/src/proguard/gui/ClassSpecificationDialog.java b/src/proguard/gui/ClassSpecificationDialog.java new file mode 100644 index 000000000..38a3146c8 --- /dev/null +++ b/src/proguard/gui/ClassSpecificationDialog.java @@ -0,0 +1,546 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 proguard.classfile.ClassConstants; +import proguard.classfile.util.ClassUtil; + +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; +import java.util.List; + +/** + * This JDialog allows the user to enter a String. + * + * @author Eric Lafortune + */ +final class ClassSpecificationDialog 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 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 JCheckBox allowShrinkingCheckBox = new JCheckBox(msg("allowShrinking")); + private final JCheckBox allowOptimizationCheckBox = new JCheckBox(msg("allowOptimization")); + private final JCheckBox allowObfuscationCheckBox = new JCheckBox(msg("allowObfuscation")); + + + private final JRadioButton[] publicRadioButtons; + private final JRadioButton[] finalRadioButtons; + private final JRadioButton[] abstractRadioButtons; + private final JRadioButton[] interfaceRadioButtons; + private final JRadioButton[] annotationRadioButtons; + private final JRadioButton[] enumRadioButtons; + private final JRadioButton[] syntheticRadioButtons; + + 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 final MemberSpecificationsPanel memberSpecificationsPanel; + + private int returnValue; + + + public ClassSpecificationDialog(JFrame owner, boolean fullKeepOptions) + { + super(owner, msg("specifyClasses"), 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 = advancedButtonConstraints.insets; + + GridBagLayout layout = new GridBagLayout(); + + Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); + + // Create the comments panel. + JPanel commentsPanel = new JPanel(layout); + commentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, + msg("comments"))); + + JScrollPane commentsScrollPane = new JScrollPane(commentsTextArea); + commentsScrollPane.setBorder(classNameTextField.getBorder()); + + commentsPanel.add(tip(commentsScrollPane, "commentsTip"), constraintsLastStretch); + + // Create the keep option panel. + ButtonGroup keepButtonGroup = new ButtonGroup(); + keepButtonGroup.add(keepClassesAndMembersRadioButton); + keepButtonGroup.add(keepClassMembersRadioButton); + keepButtonGroup.add(keepClassesWithMembersRadioButton); + + JPanel keepOptionPanel = new JPanel(layout); + keepOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, + msg("keepTitle"))); + + keepOptionPanel.add(tip(keepClassesAndMembersRadioButton, "keepTip"), constraintsLastStretch); + keepOptionPanel.add(tip(keepClassMembersRadioButton, "keepClassMembersTip"), constraintsLastStretch); + keepOptionPanel.add(tip(keepClassesWithMembersRadioButton, "keepClassesWithMembersTip"), constraintsLastStretch); + + // Create the allow option panel. + final JPanel allowOptionPanel = new JPanel(layout); + allowOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, + msg("allowTitle"))); + + allowOptionPanel.add(tip(allowShrinkingCheckBox, "allowShrinkingTip"), constraintsLastStretch); + allowOptionPanel.add(tip(allowOptimizationCheckBox, "allowOptimizationTip"), constraintsLastStretch); + allowOptionPanel.add(tip(allowObfuscationCheckBox, "allowObfuscationTip"), constraintsLastStretch); + + // Create the access panel. + JPanel accessPanel = new JPanel(layout); + 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); + finalRadioButtons = addRadioButtonTriplet("Final", accessPanel); + abstractRadioButtons = addRadioButtonTriplet("Abstract", accessPanel); + interfaceRadioButtons = addRadioButtonTriplet("Interface", accessPanel); + annotationRadioButtons = addRadioButtonTriplet("Annotation", accessPanel); + enumRadioButtons = addRadioButtonTriplet("Enum", accessPanel); + syntheticRadioButtons = addRadioButtonTriplet("Synthetic", 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, + 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"))); + + extendsAnnotationTypePanel.add(tip(extendsAnnotationTypeTextField, "classNameTip"), constraintsLastStretch); + + // Create the extends class name panel. + JPanel extendsClassNamePanel = new JPanel(layout); + extendsClassNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, + msg("extendsImplementsClass"))); + + extendsClassNamePanel.add(tip(extendsClassNameTextField, "classNameTip"), constraintsLastStretch); + + + // Create the class member list panel. + 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(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(commentsPanel, "commentsTip"), panelConstraints); + if (fullKeepOptions) + { + mainPanel.add(tip(keepOptionPanel, "keepTitleTip"), panelConstraints); + mainPanel.add(tip(allowOptionPanel, "allowTitleTip"), panelConstraints); + } + 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(tip(advancedButton, "advancedTip"), advancedButtonConstraints); + mainPanel.add(okButton, okButtonConstraints); + mainPanel.add(cancelButton, cancelButtonConstraints); + + getContentPane().add(new JScrollPane(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 KeepClassSpecification to be represented in this dialog. + */ + public void setKeepSpecification(KeepClassSpecification keepClassSpecification) + { + boolean markClasses = keepClassSpecification.markClasses; + boolean markConditionally = keepClassSpecification.markConditionally; + boolean allowShrinking = keepClassSpecification.allowShrinking; + boolean allowOptimization = keepClassSpecification.allowOptimization; + boolean allowObfuscation = keepClassSpecification.allowObfuscation; + + // Figure out the proper keep radio button and set it. + JRadioButton keepOptionRadioButton = + markConditionally ? keepClassesWithMembersRadioButton : + markClasses ? keepClassesAndMembersRadioButton : + keepClassMembersRadioButton; + + keepOptionRadioButton.setSelected(true); + + // Set the allow radio buttons. + allowShrinkingCheckBox .setSelected(allowShrinking); + allowOptimizationCheckBox.setSelected(allowOptimization); + allowObfuscationCheckBox .setSelected(allowObfuscation); + + setClassSpecification(keepClassSpecification); + } + + + /** + * 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_ABSTRACT, abstractRadioButtons); + setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE, interfaceRadioButtons); + setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ANNOTATTION, annotationRadioButtons); + setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ENUM, enumRadioButtons); + setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_SYNTHETIC, syntheticRadioButtons); + + // 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. + memberSpecificationsPanel.setMemberSpecifications(keepFieldOptions, keepMethodOptions); + } + + + /** + * Returns the KeepClassSpecification currently represented in this dialog. + */ + public KeepClassSpecification getKeepSpecification() + { + boolean markClasses = !keepClassMembersRadioButton .isSelected(); + boolean markConditionally = keepClassesWithMembersRadioButton.isSelected(); + boolean allowShrinking = allowShrinkingCheckBox .isSelected(); + boolean allowOptimization = allowOptimizationCheckBox .isSelected(); + boolean allowObfuscation = allowObfuscationCheckBox .isSelected(); + + return new KeepClassSpecification(markClasses, + markConditionally, + allowShrinking, + allowOptimization, + allowObfuscation, + getClassSpecification()); + } + + + /** + * Returns the ClassSpecification currently represented in this dialog. + */ + public ClassSpecification getClassSpecification() + { + String comments = commentsTextArea.getText(); + String annotationType = annotationTypeTextField.getText(); + String className = classNameTextField.getText(); + String extendsAnnotationType = extendsAnnotationTypeTextField.getText(); + String extendsClassName = extendsClassNameTextField.getText(); + + ClassSpecification classSpecification = + 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); + getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_FINAL, finalRadioButtons); + getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT, abstractRadioButtons); + getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE, interfaceRadioButtons); + getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ANNOTATTION, annotationRadioButtons); + getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ENUM, enumRadioButtons); + getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_SYNTHETIC, syntheticRadioButtons); + + // Get the keep class member option lists. + classSpecification.fieldSpecifications = memberSpecificationsPanel.getMemberSpecifications(true); + classSpecification.methodSpecifications = memberSpecificationsPanel.getMemberSpecifications(false); + + return classSpecification; + } + + + /** + * Shows this dialog. This method only returns when the dialog is closed. + * + * @return CANCEL_OPTION or APPROVE_OPTION, + * 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 setClassSpecificationRadioButtons(ClassSpecification classSpecification, + int flag, + JRadioButton[] radioButtons) + { + int index = (classSpecification.requiredSetAccessFlags & flag) != 0 ? 0 : + (classSpecification.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 getClassSpecificationRadioButtons(ClassSpecification classSpecification, + int flag, + JRadioButton[] radioButtons) + { + if (radioButtons[0].isSelected()) + { + classSpecification.requiredSetAccessFlags |= flag; + } + else if (radioButtons[1].isSelected()) + { + 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 new file mode 100644 index 000000000..bc2924734 --- /dev/null +++ b/src/proguard/gui/ClassSpecificationsPanel.java @@ -0,0 +1,231 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassSpecification; +import proguard.classfile.util.ClassUtil; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; + + +/** + * This ListPanel allows the user to add, edit, move, and remove + * ClassSpecification entries in a list. + * + * @author Eric Lafortune + */ +class ClassSpecificationsPanel extends ListPanel +{ + protected final ClassSpecificationDialog classSpecificationDialog; + + + public ClassSpecificationsPanel(JFrame owner, boolean fullKeepOptions) + { + super(); + + list.setCellRenderer(new MyListCellRenderer()); + + classSpecificationDialog = new ClassSpecificationDialog(owner, fullKeepOptions); + + addAddButton(); + addEditButton(); + addRemoveButton(); + addUpButton(); + addDownButton(); + + enableSelectionButtons(); + } + + + protected void addAddButton() + { + JButton addButton = new JButton(msg("add")); + addButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + setClassSpecification(createClassSpecification()); + int returnValue = classSpecificationDialog.showDialog(); + if (returnValue == ClassSpecificationDialog.APPROVE_OPTION) + { + // Add the new element. + addElement(getClassSpecification()); + } + } + }); + + addButton(tip(addButton, "addTip")); + } + + + protected void addEditButton() + { + JButton editButton = new JButton(msg("edit")); + editButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ClassSpecification selectedClassSpecification = + (ClassSpecification)list.getSelectedValue(); + + setClassSpecification(selectedClassSpecification); + int returnValue = classSpecificationDialog.showDialog(); + if (returnValue == ClassSpecificationDialog.APPROVE_OPTION) + { + // Replace the old element. + setElementAt(getClassSpecification(), + list.getSelectedIndex()); + } + } + }); + + addButton(tip(editButton, "editTip")); + } + + + protected ClassSpecification createClassSpecification() + { + return new ClassSpecification(); + } + + + protected void setClassSpecification(ClassSpecification classSpecification) + { + classSpecificationDialog.setClassSpecification(classSpecification); + } + + + protected ClassSpecification getClassSpecification() + { + return classSpecificationDialog.getClassSpecification(); + } + + + /** + * Sets the ClassSpecification objects to be represented in this panel. + */ + public void setClassSpecifications(List classSpecifications) + { + listModel.clear(); + + if (classSpecifications != null) + { + for (int index = 0; index < classSpecifications.size(); index++) + { + listModel.addElement(classSpecifications.get(index)); + } + } + + // Make sure the selection buttons are properly enabled, + // since the clear method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + /** + * Returns the ClassSpecification objects currently represented in this panel. + */ + public List getClassSpecifications() + { + int size = listModel.size(); + if (size == 0) + { + return null; + } + + List classSpecifications = new ArrayList(size); + for (int index = 0; index < size; index++) + { + classSpecifications.add(listModel.get(index)); + } + + return classSpecifications; + } + + + /** + * 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 + { + private final JLabel label = new JLabel(); + + + // Implementations for ListCellRenderer. + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + ClassSpecification classSpecification = (ClassSpecification)value; + + String comments = classSpecification.comments; + + 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) + { + label.setBackground(list.getSelectionBackground()); + label.setForeground(list.getSelectionForeground()); + } + else + { + label.setBackground(list.getBackground()); + label.setForeground(list.getForeground()); + } + + label.setOpaque(true); + + return label; + } + } +} diff --git a/src/proguard/gui/ExtensionFileFilter.java b/src/proguard/gui/ExtensionFileFilter.java new file mode 100644 index 000000000..ee097147b --- /dev/null +++ b/src/proguard/gui/ExtensionFileFilter.java @@ -0,0 +1,78 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.gui; + +import javax.swing.filechooser.FileFilter; +import java.io.File; + + +/** + * This FileFilter accepts files that end in one of the given + * extensions. + * + * @author Eric Lafortune + */ +final class ExtensionFileFilter extends FileFilter +{ + private final String description; + private final String[] extensions; + + + /** + * Creates a new ExtensionFileFilter. + * @param description a description of the filter. + * @param extensions an array of acceptable extensions. + */ + public ExtensionFileFilter(String description, String[] extensions) + { + this.description = description; + this.extensions = extensions; + } + + + // Implemntations for FileFilter + + public String getDescription() + { + return description; + } + + + public boolean accept(File file) + { + if (file.isDirectory()) + { + return true; + } + + String fileName = file.getName().toLowerCase(); + + for (int index = 0; index < extensions.length; index++) + { + if (fileName.endsWith(extensions[index])) + { + return true; + } + } + + return false; + } +} diff --git a/src/proguard/gui/FilterBuilder.java b/src/proguard/gui/FilterBuilder.java new file mode 100644 index 000000000..c362cb695 --- /dev/null +++ b/src/proguard/gui/FilterBuilder.java @@ -0,0 +1,208 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.gui; + +import javax.swing.*; + +/** + * This class builds filters corresponding to the selections and names of a + * given list of check boxes. + */ +public class FilterBuilder +{ + private JCheckBox[] checkBoxes; + private char separator; + + + /** + * Creates a new FilterBuilder. + * @param checkBoxes the check boxes with names and selections that should + * be reflected in the output filter. + * @param separator the separator for the names in the check boxes. + */ + public FilterBuilder(JCheckBox[] checkBoxes, char separator) + { + this.checkBoxes = checkBoxes; + this.separator = separator; + } + + + /** + * Builds a filter for the current names and selections of the check boxes. + */ + public String buildFilter() + { + StringBuffer positive = new StringBuffer(); + StringBuffer negative = new StringBuffer(); + + buildFilter("", positive, negative); + + return positive.length() <= negative.length() ? + positive.toString() : + negative.toString(); + } + + + /** + * Builds two versions of the filter for the given prefix. + * @param prefix the prefix. + * @param positive the filter to be extended, assuming the matching + * strings are accepted. + * @param negative the filter to be extended, assuming the matching + * strings are rejected. + */ + private void buildFilter(String prefix, + StringBuffer positive, + StringBuffer negative) + { + int positiveCount = 0; + int negativeCount = 0; + + // Count all selected and unselected check boxes with the prefix. + for (int index = 0; index < checkBoxes.length; index++) + { + JCheckBox checkBox = checkBoxes[index]; + String name = checkBox.getText(); + + if (name.startsWith(prefix)) + { + if (checkBox.isSelected()) + { + positiveCount++; + } + else + { + negativeCount++; + } + } + } + + // Are there only unselected check boxes? + if (positiveCount == 0) + { + // Extend the positive filter with exceptions and return. + if (positive.length() > 0) + { + positive.append(','); + } + positive.append('!').append(prefix); + if (prefix.length() == 0 || + prefix.charAt(prefix.length()-1) == separator) + { + positive.append('*'); + } + + return; + } + + // Are there only selected check boxes? + if (negativeCount == 0) + { + // Extend the negative filter with exceptions and return. + if (negative.length() > 0) + { + negative.append(','); + } + negative.append(prefix); + if (prefix.length() == 0 || + prefix.charAt(prefix.length()-1) == separator) + { + negative.append('*'); + } + + return; + } + + // Create new positive and negative filters for names starting with the + // prefix only. + StringBuffer positiveFilter = new StringBuffer(); + StringBuffer negativeFilter = new StringBuffer(); + + String newPrefix = null; + + for (int index = 0; index < checkBoxes.length; index++) + { + String name = checkBoxes[index].getText(); + + if (name.startsWith(prefix)) + { + if (newPrefix == null || + !name.startsWith(newPrefix)) + { + int prefixIndex = + name.indexOf(separator, prefix.length()+1); + + newPrefix = prefixIndex >= 0 ? + name.substring(0, prefixIndex+1) : + name; + + buildFilter(newPrefix, + positiveFilter, + negativeFilter); + } + } + } + + // Extend the positive filter. + if (positiveFilter.length() <= negativeFilter.length() + prefix.length() + 3) + { + if (positive.length() > 0 && + positiveFilter.length() > 0) + { + positive.append(','); + } + + positive.append(positiveFilter); + } + else + { + if (positive.length() > 0 && + negativeFilter.length() > 0) + { + positive.append(','); + } + + positive.append(negativeFilter).append(",!").append(prefix).append('*'); + } + + // Extend the negative filter. + if (negativeFilter.length() <= positiveFilter.length() + prefix.length() + 4) + { + if (negative.length() > 0 && + negativeFilter.length() > 0) + { + negative.append(','); + } + + negative.append(negativeFilter); + } + else + { + if (negative.length() > 0 && + positiveFilter.length() > 0) + { + negative.append(','); + } + + negative.append(positiveFilter).append(',').append(prefix).append('*'); + } + } +} diff --git a/src/proguard/gui/FilterDialog.java b/src/proguard/gui/FilterDialog.java new file mode 100644 index 000000000..88dbd8bb0 --- /dev/null +++ b/src/proguard/gui/FilterDialog.java @@ -0,0 +1,320 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.ListUtil; + +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; +import java.util.List; + +/** + * This JDialog allows the user to enter a String. + * + * @author Eric Lafortune + */ +public class FilterDialog 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 static final String DEFAULT_FILTER = "**"; + private static final String DEFAULT_JAR_FILTER = "**.jar"; + private static final String DEFAULT_WAR_FILTER = "**.war"; + private static final String DEFAULT_EAR_FILTER = "**.ear"; + private static final String DEFAULT_ZIP_FILTER = "**.zip"; + + + 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; + + + public FilterDialog(JFrame owner, + String explanation) + { + super(owner, true); + setResizable(true); + + // Create some constraints that can be reused. + GridBagConstraints textConstraints = new GridBagConstraints(); + textConstraints.gridwidth = GridBagConstraints.REMAINDER; + textConstraints.fill = GridBagConstraints.HORIZONTAL; + textConstraints.weightx = 1.0; + textConstraints.weighty = 1.0; + textConstraints.anchor = GridBagConstraints.NORTHWEST; + textConstraints.insets = new Insets(10, 10, 10, 10); + + GridBagConstraints labelConstraints = new GridBagConstraints(); + labelConstraints.anchor = GridBagConstraints.WEST; + labelConstraints.insets = new Insets(1, 2, 1, 2); + + GridBagConstraints textFieldConstraints = new GridBagConstraints(); + textFieldConstraints.gridwidth = GridBagConstraints.REMAINDER; + textFieldConstraints.fill = GridBagConstraints.HORIZONTAL; + textFieldConstraints.weightx = 1.0; + textFieldConstraints.anchor = GridBagConstraints.WEST; + textFieldConstraints.insets = labelConstraints.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 = 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); + + // Create the panel with the explanation. + JTextArea explanationTextArea = new JTextArea(explanation, 3, 0); + explanationTextArea.setOpaque(false); + explanationTextArea.setEditable(false); + explanationTextArea.setLineWrap(true); + explanationTextArea.setWrapStyleWord(true); + + // Create the filter labels. + 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, + msg("filters"))); + + filterPanel.add(explanationTextArea, textConstraints); + + filterPanel.add(tip(filterLabel, "nameFilterTip"), labelConstraints); + filterPanel.add(tip(filterTextField, "fileNameFilterTip"), textFieldConstraints); + + filterPanel.add(tip(jarFilterLabel, "jarNameFilterTip"), labelConstraints); + filterPanel.add(tip(jarFilterTextField, "fileNameFilterTip"), textFieldConstraints); + + filterPanel.add(tip(warFilterLabel, "warNameFilterTip"), labelConstraints); + filterPanel.add(tip(warFilterTextField, "fileNameFilterTip"), textFieldConstraints); + + filterPanel.add(tip(earFilterLabel, "earNameFilterTip"), labelConstraints); + filterPanel.add(tip(earFilterTextField, "fileNameFilterTip"), textFieldConstraints); + + filterPanel.add(tip(zipFilterLabel, "zipNameFilterTip"), labelConstraints); + filterPanel.add(tip(zipFilterTextField, "fileNameFilterTip"), textFieldConstraints); + + + JButton okButton = new JButton(msg("ok")); + okButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + returnValue = APPROVE_OPTION; + hide(); + } + }); + + 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(filterPanel, panelConstraints); + mainPanel.add(okButton, okButtonConstraints); + mainPanel.add(cancelButton, cancelButtonConstraints); + + getContentPane().add(mainPanel); + } + + + /** + * Sets the filter to be represented in this dialog. + */ + public void setFilter(List filter) + { + filterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter, true) : DEFAULT_FILTER); + } + + + /** + * Returns the filter currently represented in this dialog. + */ + public List getFilter() + { + String filter = filterTextField.getText(); + + return filter.equals(DEFAULT_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Sets the jar filter to be represented in this dialog. + */ + public void setJarFilter(List filter) + { + jarFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter, true) : DEFAULT_JAR_FILTER); + } + + + /** + * Returns the jar filter currently represented in this dialog. + */ + public List getJarFilter() + { + String filter = jarFilterTextField.getText(); + + return filter.equals(DEFAULT_JAR_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Sets the war filter to be represented in this dialog. + */ + public void setWarFilter(List filter) + { + warFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter, true) : DEFAULT_WAR_FILTER); + } + + + /** + * Returns the war filter currently represented in this dialog. + */ + public List getWarFilter() + { + String filter = warFilterTextField.getText(); + + return filter.equals(DEFAULT_WAR_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Sets the ear filter to be represented in this dialog. + */ + public void setEarFilter(List filter) + { + earFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter, true) : DEFAULT_EAR_FILTER); + } + + + /** + * Returns the ear filter currently represented in this dialog. + */ + public List getEarFilter() + { + String filter = earFilterTextField.getText(); + + return filter.equals(DEFAULT_EAR_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Sets the zip filter to be represented in this dialog. + */ + public void setZipFilter(List filter) + { + zipFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter, true) : DEFAULT_ZIP_FILTER); + } + + + /** + * Returns the zip filter currently represented in this dialog. + */ + public List getZipFilter() + { + String filter = zipFilterTextField.getText(); + + return filter.equals(DEFAULT_ZIP_FILTER) ? null : ListUtil.commaSeparatedList(filter); + } + + + /** + * Shows this dialog. This method only returns when the dialog is closed. + * + * @return CANCEL_OPTION or APPROVE_OPTION, + * depending on the choice of the user. + */ + public int showDialog() + { + returnValue = CANCEL_OPTION; + + // Open the dialog in the right place, then wait for it to be closed, + // one way or another. + pack(); + setLocationRelativeTo(getOwner()); + show(); + + return returnValue; + } + + + /** + * Attaches the tool tip from the GUI resources that corresponds to the + * given key, to the given component. + */ + private static JComponent tip(JComponent component, String messageKey) + { + component.setToolTipText(msg(messageKey)); + + return component; + } + + + /** + * Returns the message from the GUI resources that corresponds to the given + * key. + */ + private static String msg(String messageKey) + { + return GUIResources.getMessage(messageKey); + } +} diff --git a/src/proguard/gui/GUIResources.java b/src/proguard/gui/GUIResources.java new file mode 100644 index 000000000..3d7c6c65d --- /dev/null +++ b/src/proguard/gui/GUIResources.java @@ -0,0 +1,56 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.text.MessageFormat; +import java.util.ResourceBundle; + + +/** + * This class provides some utility methods for working with resource bundles. + * + * @author Eric Lafortune + */ +class GUIResources +{ + private static final ResourceBundle messages = ResourceBundle.getBundle(GUIResources.class.getName()); + private static final MessageFormat formatter = new MessageFormat(""); + + + /** + * Returns an internationalized message, based on its key. + */ + public static String getMessage(String messageKey) + { + return messages.getString(messageKey); + } + + + /** + * Returns an internationalized, formatted message, based on its key, with + * the given arguments. + */ + public static String getMessage(String messageKey, Object[] messageArguments) + { + formatter.applyPattern(messages.getString(messageKey)); + return formatter.format(messageArguments); + } +} diff --git a/src/proguard/gui/GUIResources.properties b/src/proguard/gui/GUIResources.properties new file mode 100644 index 000000000..5ab67de8b --- /dev/null +++ b/src/proguard/gui/GUIResources.properties @@ -0,0 +1,643 @@ +# ProGuard -- shrinking, optimization, and obfuscation of Java class files. +# Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + +# +# Tab names. +# +proGuardTab = ProGuard +inputOutputTab = Input/Output +shrinkingTab = Shrinking +obfuscationTab = Obfuscation +optimizationTab = Optimization +informationTab = Information +processTab = Process +reTraceTab = ReTrace + +# +# Splash text. +# +developed = Developed by Eric Lafortune +shrinking = Shrinking +optimization = Optimization +obfuscation = Obfuscation +preverification = Preverification + +# +# Panel titles. +# +welcome = Welcome to ProGuard, version 4.9 +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 +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 = \ + Optionally specify additional methods that don't have any side effects.
\ + Only add entries if you know what you're doing! +whyAreYouKeepingTip = \ + Ask ProGuard why it is keeping certain classes, fields, or methods. + +# +# Info texts. +# +proGuardInfo = \ + ProGuard is a free class file shrinker, optimizer, obfuscator, and preverifier.\ +

\ + With this GUI, you can create, load, modify, and save ProGuard configurations.\ +
\ + You can then process your code right away, or you can run ProGuard from the \ + command line using your saved configuration.\ +

\ + With the ReTrace part of this GUI you can de-obfuscate your stack traces.\ +

\ + ProGuard and ReTrace are written and maintained by Eric Lafortune.\ +

\ + Official site at Sourceforge: \ + http://proguard.sourceforge.net/\ +
\ + Professional support by Saikoa: \ + http://www.saikoa.com/\ +

\ + Distributed under the GNU General Public License.\ +
\ + Copyright © 2002-2013. + +processingInfo = \ + You can now start processing your code, \ + or you can run ProGuard from the command line using your saved configuration. + +reTraceInfo = \ + If you had ProGuard write out a mapping file, \ + you can de-obfuscate your obfuscated stack traces with ReTrace!\ + \n\n\ + You can load an obfuscated stack trace from a file, \ + or you can paste it straight into the text area above. + +# +# Titles and labels corresponding to common ProGuard options. +# +programJars = Program jars, wars, ears, zips, and directories +libraryJars = Library jars, wars, ears, zips, and directories + +shrink = Shrink +printUsage = Print usage + +optimize = Optimize +allowAccessModification = Allow access modification +mergeInterfacesAggressively = Merge interfaces aggressively +optimizations = Optimizations +optimizationPasses = Optimization passes + +obfuscate = Obfuscate +printMapping = Print mapping +applyMapping = Apply mapping +obfuscationDictionary = Obfuscation dictionary +classObfuscationDictionary = Class obfuscation dictionary +packageObfuscationDictionary = Package obfuscation dictionary +overloadAggressively = Overload aggressively +useUniqueClassMemberNames = Use unique class member names +keepPackageNames = Keep package names +flattenPackageHierarchy = Flatten package hierarchy +repackageClasses = Repackage classes +useMixedCaseClassNames = Use mixed-case class names +keepAttributes = Keep attributes +keepParameterNames = Keep parameter names +renameSourceFileAttribute = Rename SourceFile attribute +adaptClassStrings = Adapt class strings +adaptResourceFileNames = Adapt resource file names +adaptResourceFileContents = Adapt resource file contents + +preverify = Preverify +microEdition = Micro Edition + +verbose = Verbose +note = Note potential mistakes in the configuration +warn = Warn about possibly erroneous input +ignoreWarnings = Ignore warnings about possibly erroneous input +skipNonPublicLibraryClasses = Skip non-public library classes +skipNonPublicLibraryClassMembers = Skip non-public library class members +keepDirectories = Keep directories +forceProcessing = Force processing +target = Target +targets = 1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7 +printSeeds = Print seeds +printConfiguration = Print configuration +dump = Print class files + +mappingFile = Mapping file +obfuscatedStackTrace = Obfuscated stack trace + +programJarsTip = \ + The input jars (wars, ears, zips, directories), followed by
\ + their corresponding output jars (wars, ears, zips, directories). +libraryJarsTip = \ + The library jars (wars, ears, zips, directories), on which the program jars depend.
\ + The library jars are required for processing, but they are not copied to the output. + +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. +mergeInterfacesAggressivelyTip = \ + Allow interfaces to be merged, even if their implementations don't implement all
\ + interface methods. This is not allowed in the Java language, but it is allowed in bytecode. +optimizationsTip = \ + Specify the types of optimizations to be performed. +optimizationsFilterTip = \ + A filter for the names of the optimizations to be performed. +optimizationsSelectTip = \ + Select from the currently available optimizations... +optimizationPassesTip = \ + Specify 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. +classObfuscationDictionaryTip = \ + Use the words in the given file for obfuscating class names. +packageObfuscationDictionaryTip = \ + Use the words in the given file for obfuscating package names. +overloadAggressivelyTip = \ + Allow fields and methods to get the same obfuscated names, even if only their types or
\ + return types differ. This is not allowed in the Java language, but it is allowed in bytecode. +useUniqueClassMemberNamesTip = \ + Make sure fields and methods get the same obfuscation mapping across classes, even
\ + if they are unrelated. This is advisable if the output is to be obfuscated incrementally. +keepPackageNamesTip = \ + Keep the specified package names from being obfuscated. +packageNamesTip = \ + An optional comma-separated list of package names,
\ + e.g. myapplication,mylibrary.**
\ + Possible wildcards:\ +

    \ +
  • ? for any single character, except the package separator.\ +
  • * for any number of any characters, except the package separator.\ +
  • ** for any number of any characters.\ +
\ + The negator ! is also supported. +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 optional package name. +useMixedCaseClassNamesTip = \ + Generate mixed-case obfucated class names. This will complicate unpacking
\ + the resulting jars on case-insensitive file systems, should that be necessary. +keepAttributesTip = \ + Keep the specified optional class file attributes. +attributesTip = \ + An optional comma-separated list of class file attributes.\ +
    \ +
  • "Exceptions,Innerclasses, Signature" are necessary if the output is to be used as a library.\ +
  • "Deprecated" is optional if the output is to be used as a library.\ +
  • "LocalVariable*Table" can be useful for debugging.\ +
  • "Sourcefile,LineNumberTable" are necessary for generating stack traces.\ +
  • "*Annotations*" is necessary for preserving annotations.\ +
\ + The wildcard * and the negator ! are allowed. +keepParameterNamesTip = \ + Keep parameter names and types in "LocalVariable*Table" attributes
\ + in methods that are not obfuscated. +renameSourceFileAttributeTip = \ + Put the given string in the "SourceFile" attribute of the processed class files.
\ + It will appear as the file name of the classes in stack traces. +sourceFileAttributeTip = \ + The replacement "SourceFile" string. +adaptClassStringsTip = \ + Adapt string constants in the specified classes, based
\ + on the obfuscated names of corresponding classes. +adaptResourceFileNamesTip = \ + Rename the specified resource files, based on the
\ + obfuscated names of the corresponding class files. +adaptResourceFileContentsTip = \ + Adapt the contents of the specified resource files, based
\ + on the obfuscated names of the processed classes. +fileNameFilterTip = \ + A filter on file names,
\ + e.g. mydirectory1/**,mydirectory2/**
\ + Possible wildcards:\ +
    \ +
  • ? for any single character, except the directory separator.\ +
  • * for any number of any characters, except the directory separator.\ +
  • ** for any number of any characters.\ +
\ + The negator ! is also supported. + +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. +noteFilterTip = \ + A filter matching classes for which no notes should be printed. +warnTip = \ + Print out warnings about possibly erroneous input.
\ + Only unset this option if you know what you're doing! +warnFilterTip = \ + A filter matching classes for which no warnings should be printed. +ignoreWarningsTip = \ + Ignore any warnings about possibly erroneous input.
\ + Only set this option if you know what you're doing! +skipNonPublicLibraryClassesTip = \ + Skip reading non-public library classes, for efficiency.
\ + You may have to unset this option if ProGuard complains about missing classes. +skipNonPublicLibraryClassMembersTip = \ + Skip reading non-public library fields and methods, for efficiency.
\ + You may have to unset this option if ProGuard complains about missing class members. +keepDirectoriesTip = \ + Keep the specified directories in the output jars, wars, ears, zips, or directories. +directoriesTip = \ + A filter on directory names,
\ + e.g. mydirectory1,mydirectory2/**
\ + Possible wildcards:\ +
    \ +
  • ? for any single character, except the directory separator.\ +
  • * for any number of any characters, except the directory separator.\ +
  • ** for any number of any characters.\ +
\ + The negator ! is also supported. +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. +# +keepTitle = Keep + +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 = \ + Keep the specified classes, fields, and methods as entry points.
\ + This is the most common option. +keepClassMembersTip = \ + Only keep the specified fields and methods as entry points. +keepClassesWithMembersTip = \ + Keep the specified classes, fields, and methods,
\ + on the condition that the fields and methods are present. + +allowTitleTip = \ + Optionally relax keeping the specified classes, fields, and methods.
\ + These are advanced options. + +allowShrinkingTip = \ + Remove the specified classes, fields, and methods anyway, if they are not used. +allowOptimizationTip = \ + Optimize the specified classes, fields, and methods as entry points anyway.
\ + Only set this option if you know what you're doing! +allowObfuscationTip = \ + Obfuscate the names of the specified classes, fields, and methods anyway.
\ + Only set this option if you know what you're doing! + +# +# Further keep titles and labels. +# +specifyClasses = Specify classes and class members... +specifyFields = Specify fields... +specifyMethods = Specify methods... + +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 # + +fieldType = Field type +returnType = Return type +name = Name +argumentTypes = Argument types + +commentsTip = \ + Optionally add a comment for this option in the configuration file. +accessTip = \ + Optionally place constraints on the access modifiers of this element.
\ + E.g. only match public elements. +requiredTip = \ + The access modifier has to be set. +notTip = \ + The access modifier must not be set. +dontCareTip = \ + The access modifier is irrelevant. +annotationTip = \ + Optionally require the given annotation to be present on this element.
\ + E.g. only match elements that have an annotation myPackage.MyAnnotation.
\ + This is an advanced option. +classTip = \ + The name of the class or interface. +extendsImplementsAnnotationTip = \ + Optionally require the given annotation to be present on the
\ + extended or implemented class or interface.
\ + E.g. only match classes that extend a class that has an annotation
\ + myPackage.MyAnnotation.
\ + This is an advanced option. +extendsImplementsClassTip = \ + Optionally require the class to implement or extend the given class or interface.
\ + E.g. only match classes that implement an interface myPackage.MyInterface. +classMembersTip = \ + Optionally keep fields and methods as entry points in the matching class or classes.
\ + E.g. keep all public 'get*' methods as entry points. + +fieldTypeTip = The field type. +returnTypeTip = The method return type, if any. +nameTip = The name. +argumentTypesTip = The method argument types, if any. + +classNameTip = \ + The class name, e.g. myPackage.MyClass
\ + Possible wildcards:\ +
    \ +
  • ? for any single character, except the package separator.\ +
  • * for any number of any characters, except the package separator.\ +
  • ** for any number of any characters.\ +
+classNamesTip = \ + A regular expression to further constrain the class names,
\ + e.g. myPackage1.MyClass,myPackage2.**
\ + Possible wildcards:\ +
    \ +
  • ? for any single character, except the package separator.\ +
  • * for any number of any characters, except the package separator.\ +
  • ** for any number of any characters.\ +
\ + The negator ! is also supported. +typeTip = \ + The type, e.g. int, or java.lang.String[]
\ + Possible wildcards:\ +
    \ +
  • % for any primitive type.\ +
  • ? for any single character, except the package separator.\ +
  • * for any number of any characters, except the package separator.\ +
  • ** for any number of any characters.\ +
  • *** (or empty) for any type.\ +
+fieldNameTip = \ + The field name, e.g. myField
\ + Possible wildcards:\ +
    \ +
  • ? for any single character.\ +
  • * for any number of any characters.\ +
+methodNameTip = \ + The method name, e.g. myMethod
\ + Possible wildcards:\ +
    \ +
  • ? for any single character.\ +
  • * for any number of any characters.\ +
+argumentTypes2Tip = \ + The comma-separated list of argument types,
\ + e.g. java.lang.String[],int,boolean
\ + Possible wildcards:\ +
    \ +
  • % for any primitive type.\ +
  • ? for any single character, except the package separator.\ +
  • * for any number of any characters, except the package separator.\ +
  • ** for any number of any characters.\ +
  • *** for any type.\ +
  • ... for any number of any arguments.\ +
+ +# +# Titles and labels corresponding to optimization options. +# +selectOptimizations = Select optimizations... + +field = Field +method = Method +code = Code + +class_marking_finalTip = \ + Mark classes as final, whenever possible. +class_merging_verticalTip = \ + Merge classes vertically in the class hierarchy, whenever possible. +class_merging_horizontalTip = \ + Merge classes horizontally in the class hierarchy, whenever possible. +field_removal_writeonlyTip = \ + Remove write-only fields. +field_marking_privateTip = \ + Mark fields as private, whenever possible. +field_propagation_valueTip = \ + Propagate the values of fields across methods. +method_marking_privateTip = \ + Mark methods as private, whenever possible (devirtualization). +method_marking_staticTip = \ + Mark methods as static, whenever possible (devirtualization). +method_marking_finalTip = \ + Mark methods as final, whenever possible. +method_removal_parameterTip = \ + Remove unused method parameters. +method_propagation_parameterTip = \ + Propagate the values of method parameters from method invocations to \ + the invoked methods. +method_propagation_returnvalueTip = \ + Propagate the values of method return values from methods to their \ + invocations. +method_inlining_shortTip = \ + Inline short methods. +method_inlining_uniqueTip = \ + Inline methods that are only called once. +method_inlining_tailrecursionTip = \ + Simplify tail recursion calls, whenever possible. +code_mergingTip = \ + Merge identical blocks of code by modifying branch targets. +code_simplification_variableTip = \ + Perform peephole optimizations for variable loading and storing. +code_simplification_arithmeticTip = \ + Perform peephole optimizations for arithmetic instructions. +code_simplification_castTip = \ + Perform peephole optimizations for casting operations. +code_simplification_fieldTip = \ + Perform peephole optimizations for field loading and storing. +code_simplification_branchTip = \ + Perform peephole optimizations for branch instructions. +code_simplification_stringTip = \ + Perform peephole optimizations for constant strings. +code_simplification_advancedTip = \ + Simplify code based on control flow analysis and data flow analysis. +code_removal_advancedTip = \ + Remove dead code based on control flow analysis and data flow analysis. +code_removal_simpleTip = \ + Remove dead code based on a simple control flow analysis. +code_removal_variableTip = \ + Remove unused variables from the local variable frame. +code_removal_exceptionTip = \ + Remove exceptions with empty try blocks. +code_allocation_variableTip = \ + Optimize variable allocation on the local variable frame. + + +# +# File selection titles. +# +selectConfigurationFile = Select a configuration file... +saveConfigurationFile = Save configuration... +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}] +cantParseConfigurationFile = Can''t parse the configuration file [{0}] +cantSaveConfigurationFile = Can''t save the configuration file [{0}] +cantOpenStackTraceFile = Can''t open the stack trace file [{0}] + +jarWarEarZipExtensions = *.jar, *.war, *.ear, *.zip (archives and directories) +proExtension = *.pro (ProGuard configurations) + +addJars = Add one or more jars or directories... +chooseJars = Choose different jars or directories... +enterFilter = Optionally filter the file names contained in the selected entries. + +filters = Filters +nameFilter = File name filter +jarNameFilter = Jar name filter +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 +selectAll = Select all +selectNone = Select none +ok = Ok +cancel = Cancel + +add = Add... +addInput = Add input... +addOutput = Add output... +edit = Edit... +filter = Filter... +remove = Remove +moveUp = Move up +moveDown = Move down + +moveToLibraries = Move to libraries +moveToProgram = Move to program + +addField = Add field... +addMethod = Add method... + +select = Select... + +loadConfiguration = Load configuration... +viewConfiguration = View configuration +saveConfiguration = Save configuration... +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. +# +warning = Warning +outOfMemory = Out of memory +outOfMemoryInfo = \n\ + You should run the ProGuard GUI with a larger java heap size, \ + with a command like\ + \n\n\t\ + java -Xms128m -Xmx192m -jar proguardgui.jar {0}\ + \n\n\ + or you can try running ProGuard from the command line. \ + with a command like\ + \n\n\t\ + java -jar proguard.jar @{0} +sampleConfigurationFileName = configuration.pro +errorProcessing = Error during processing +errorReTracing = Error during retracing diff --git a/src/proguard/gui/KeepSpecificationsPanel.java b/src/proguard/gui/KeepSpecificationsPanel.java new file mode 100644 index 000000000..908f02895 --- /dev/null +++ b/src/proguard/gui/KeepSpecificationsPanel.java @@ -0,0 +1,81 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ListPanel allows the user to add, edit, move, and remove + * KeepClassSpecification 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 KeepClassSpecification(markClasses, + markConditionally, + allowShrinking, + allowOptimization, + allowObfuscation); + } + + + protected void setClassSpecification(ClassSpecification classSpecification) + { + classSpecificationDialog.setKeepSpecification((KeepClassSpecification)classSpecification); + } + + + protected ClassSpecification getClassSpecification() + { + return classSpecificationDialog.getKeepSpecification(); + } +} diff --git a/src/proguard/gui/ListPanel.java b/src/proguard/gui/ListPanel.java new file mode 100644 index 000000000..19a0c4dc1 --- /dev/null +++ b/src/proguard/gui/ListPanel.java @@ -0,0 +1,341 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.util.List; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.*; + +/** + * This Jpanel allows the user to move and remove entries in a + * list and between lists. Extensions of this class should add buttons to add + * and possibly edit entries, and to set and get the resulting list. + * + * @author Eric Lafortune + */ +abstract class ListPanel extends JPanel +{ + protected final DefaultListModel listModel = new DefaultListModel(); + protected final JList list = new JList(listModel); + + protected int firstSelectionButton = 2; + + + protected ListPanel() + { + GridBagLayout layout = new GridBagLayout(); + setLayout(layout); + + GridBagConstraints listConstraints = new GridBagConstraints(); + listConstraints.gridheight = GridBagConstraints.REMAINDER; + listConstraints.fill = GridBagConstraints.BOTH; + listConstraints.weightx = 1.0; + listConstraints.weighty = 1.0; + listConstraints.anchor = GridBagConstraints.NORTHWEST; + listConstraints.insets = new Insets(0, 2, 0, 2); + + // Make sure some buttons are disabled or enabled depending on whether + // the selection is empty or not. + list.addListSelectionListener(new ListSelectionListener() + { + public void valueChanged(ListSelectionEvent e) + { + enableSelectionButtons(); + } + }); + + add(new JScrollPane(list), listConstraints); + + // something like the following calls are up to the extending class: + //addAddButton(); + //addEditButton(); + //addRemoveButton(); + //addUpButton(); + //addDownButton(); + // + //enableSelectionButtons(); + } + + + protected void addRemoveButton() + { + JButton removeButton = new JButton(msg("remove")); + removeButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // Remove the selected elements. + removeElementsAt(list.getSelectedIndices()); + } + }); + + addButton(tip(removeButton, "removeTip")); + } + + + protected void addUpButton() + { + JButton upButton = new JButton(msg("moveUp")); + upButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + int[] selectedIndices = list.getSelectedIndices(); + if (selectedIndices.length > 0 && + selectedIndices[0] > 0) + { + // Move the selected elements up. + moveElementsAt(selectedIndices, -1); + } + } + }); + + addButton(tip(upButton, "moveUpTip")); + } + + + protected void addDownButton() + { + JButton downButton = new JButton(msg("moveDown")); + downButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + int[] selectedIndices = list.getSelectedIndices(); + if (selectedIndices.length > 0 && + selectedIndices[selectedIndices.length-1] < listModel.getSize()-1) + { + // Move the selected elements down. + moveElementsAt(selectedIndices, 1); + } + } + }); + + addButton(tip(downButton, "moveDownTip")); + } + + + /** + * Adds a button that allows to copy or move entries to another ListPanel. + * + * @param buttonTextKey the button text key. + * @param tipKey the tool tip key. + * @param panel the other ListPanel. + */ + public void addCopyToPanelButton(String buttonTextKey, + String tipKey, + final ListPanel panel) + { + JButton moveButton = new JButton(msg(buttonTextKey)); + moveButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + int[] selectedIndices = list.getSelectedIndices(); + Object[] selectedElements = list.getSelectedValues(); + + // Remove the selected elements from this panel. + removeElementsAt(selectedIndices); + + // Add the elements to the other panel. + panel.addElements(selectedElements); + } + }); + + addButton(tip(moveButton, tipKey)); + } + + + protected void addButton(JComponent button) + { + GridBagConstraints buttonConstraints = new GridBagConstraints(); + buttonConstraints.gridwidth = GridBagConstraints.REMAINDER; + buttonConstraints.fill = GridBagConstraints.HORIZONTAL; + buttonConstraints.anchor = GridBagConstraints.NORTHWEST; + buttonConstraints.insets = new Insets(0, 2, 0, 2); + + add(button, buttonConstraints); + } + + + /** + * Returns a list of all right-hand side buttons. + */ + public List getButtons() + { + List list = new ArrayList(getComponentCount()-1); + + // Add all buttons. + for (int index = 1; index < getComponentCount(); index++) + { + list.add(getComponent(index)); + } + + return list; + } + + + protected void addElement(Object element) + { + listModel.addElement(element); + + // Make sure it is selected. + list.setSelectedIndex(listModel.size() - 1); + } + + + protected void addElements(Object[] elements) + { + // Add the elements one by one. + for (int index = 0; index < elements.length; index++) + { + listModel.addElement(elements[index]); + } + + // Make sure they are selected. + int[] selectedIndices = new int[elements.length]; + for (int index = 0; index < selectedIndices.length; index++) + { + selectedIndices[index] = + listModel.size() - selectedIndices.length + index; + } + list.setSelectedIndices(selectedIndices); + } + + + protected void moveElementsAt(int[] indices, int offset) + { + // Remember the selected elements. + Object[] selectedElements = list.getSelectedValues(); + + // Remove the selected elements. + removeElementsAt(indices); + + // Update the element indices. + for (int index = 0; index < indices.length; index++) + { + indices[index] += offset; + } + + // Reinsert the selected elements. + insertElementsAt(selectedElements, indices); + } + + + protected void insertElementsAt(Object[] elements, int[] indices) + { + for (int index = 0; index < elements.length; index++) + { + listModel.insertElementAt(elements[index], indices[index]); + } + + // Make sure they are selected. + list.setSelectedIndices(indices); + } + + + protected void setElementAt(Object element, int index) + { + listModel.setElementAt(element, index); + + // Make sure it is selected. + list.setSelectedIndex(index); + } + + + protected void setElementsAt(Object[] elements, int[] indices) + { + for (int index = 0; index < elements.length; index++) + { + listModel.setElementAt(elements[index], indices[index]); + } + + // Make sure they are selected. + list.setSelectedIndices(indices); + } + + + protected void removeElementsAt(int[] indices) + { + for (int index = indices.length - 1; index >= 0; index--) + { + listModel.removeElementAt(indices[index]); + } + + // Make sure nothing is selected. + list.clearSelection(); + + // Make sure the selection buttons are properly enabled, + // since the above method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + protected void removeAllElements() + { + listModel.removeAllElements(); + + // Make sure the selection buttons are properly enabled, + // since the above method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + /** + * Enables or disables the buttons that depend on a selection. + */ + protected void enableSelectionButtons() + { + boolean selected = !list.isSelectionEmpty(); + + // Loop over all components, except the list itself and the Add button. + for (int index = firstSelectionButton; index < getComponentCount(); index++) + { + 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/MANIFEST.MF b/src/proguard/gui/MANIFEST.MF new file mode 100644 index 000000000..05403b491 --- /dev/null +++ b/src/proguard/gui/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: proguard.gui.ProGuardGUI +Class-Path: proguard.jar retrace.jar diff --git a/src/proguard/gui/MemberSpecificationDialog.java b/src/proguard/gui/MemberSpecificationDialog.java new file mode 100644 index 000000000..4bf72cacb --- /dev/null +++ b/src/proguard/gui/MemberSpecificationDialog.java @@ -0,0 +1,509 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 JDialog 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 final JRadioButton[] syntheticRadioButtons; + + private JRadioButton[] volatileRadioButtons; + private JRadioButton[] transientRadioButtons; + + private JRadioButton[] synchronizedRadioButtons; + private JRadioButton[] nativeRadioButtons; + private JRadioButton[] abstractRadioButtons; + private JRadioButton[] strictRadioButtons; + private JRadioButton[] bridgeRadioButtons; + private JRadioButton[] varargsRadioButtons; + + 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, msg(isField ? "specifyFields" : "specifyMethods"), 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); + syntheticRadioButtons = addRadioButtonTriplet("Synthetic", 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); + bridgeRadioButtons = addRadioButtonTriplet("Bridge", accessPanel); + varargsRadioButtons = addRadioButtonTriplet("Varargs", 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(new JScrollPane(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_SYNTHETIC, syntheticRadioButtons); + 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); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_BRIDGE, bridgeRadioButtons); + setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_VARARGS, varargsRadioButtons); + + // 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_SYNTHETIC, syntheticRadioButtons); + 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); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_BRIDGE, bridgeRadioButtons); + getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_VARARGS, varargsRadioButtons); + + return memberSpecification; + } + + + /** + * Shows this dialog. This method only returns when the dialog is closed. + * + * @return CANCEL_OPTION or APPROVE_OPTION, + * 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/MemberSpecificationsPanel.java b/src/proguard/gui/MemberSpecificationsPanel.java new file mode 100644 index 000000000..6a72a1d8b --- /dev/null +++ b/src/proguard/gui/MemberSpecificationsPanel.java @@ -0,0 +1,304 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 proguard.classfile.ClassConstants; +import proguard.classfile.util.ClassUtil; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; + + +/** + * This ListPanel allows the user to add, edit, move, and remove + * MemberSpecification entries in a list. + * + * @author Eric Lafortune + */ +final class MemberSpecificationsPanel extends ListPanel +{ + private final MemberSpecificationDialog fieldSpecificationDialog; + private final MemberSpecificationDialog methodSpecificationDialog; + + + public MemberSpecificationsPanel(JDialog owner, boolean fullKeepOptions) + { + super(); + + super.firstSelectionButton = fullKeepOptions ? 3 : 2; + + list.setCellRenderer(new MyListCellRenderer()); + + fieldSpecificationDialog = new MemberSpecificationDialog(owner, true); + methodSpecificationDialog = new MemberSpecificationDialog(owner, false); + + if (fullKeepOptions) + { + addAddFieldButton(); + } + addAddMethodButton(); + addEditButton(); + addRemoveButton(); + addUpButton(); + addDownButton(); + + enableSelectionButtons(); + } + + + protected void addAddFieldButton() + { + JButton addFieldButton = new JButton(msg("addField")); + addFieldButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + fieldSpecificationDialog.setMemberSpecification(new MemberSpecification()); + int returnValue = fieldSpecificationDialog.showDialog(); + if (returnValue == MemberSpecificationDialog.APPROVE_OPTION) + { + // Add the new element. + addElement(new MyMemberSpecificationWrapper(fieldSpecificationDialog.getMemberSpecification(), + true)); + } + } + }); + + addButton(tip(addFieldButton, "addFieldTip")); + } + + + protected void addAddMethodButton() + { + JButton addMethodButton = new JButton(msg("addMethod")); + addMethodButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + methodSpecificationDialog.setMemberSpecification(new MemberSpecification()); + int returnValue = methodSpecificationDialog.showDialog(); + if (returnValue == MemberSpecificationDialog.APPROVE_OPTION) + { + // Add the new element. + addElement(new MyMemberSpecificationWrapper(methodSpecificationDialog.getMemberSpecification(), + false)); + } + } + }); + + addButton(tip(addMethodButton, "addMethodTip")); + } + + + protected void addEditButton() + { + JButton editButton = new JButton(msg("edit")); + editButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + MyMemberSpecificationWrapper wrapper = + (MyMemberSpecificationWrapper)list.getSelectedValue(); + + MemberSpecificationDialog memberSpecificationDialog = + wrapper.isField ? + fieldSpecificationDialog : + methodSpecificationDialog; + + memberSpecificationDialog.setMemberSpecification(wrapper.memberSpecification); + int returnValue = memberSpecificationDialog.showDialog(); + if (returnValue == MemberSpecificationDialog.APPROVE_OPTION) + { + // Replace the old element. + wrapper.memberSpecification = memberSpecificationDialog.getMemberSpecification(); + setElementAt(wrapper, + list.getSelectedIndex()); + } + } + }); + + addButton(tip(editButton, "editTip")); + } + + + /** + * Sets the MemberSpecification instances to be represented in this panel. + */ + public void setMemberSpecifications(List fieldSpecifications, + List methodSpecifications) + { + listModel.clear(); + + if (fieldSpecifications != null) + { + for (int index = 0; index < fieldSpecifications.size(); index++) + { + listModel.addElement( + new MyMemberSpecificationWrapper((MemberSpecification)fieldSpecifications.get(index), + true)); + } + } + + if (methodSpecifications != null) + { + for (int index = 0; index < methodSpecifications.size(); index++) + { + listModel.addElement( + new MyMemberSpecificationWrapper((MemberSpecification)methodSpecifications.get(index), + false)); + } + } + + // Make sure the selection buttons are properly enabled, + // since the clear method doesn't seem to notify the listener. + enableSelectionButtons(); + } + + + /** + * 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 getMemberSpecifications(boolean isField) + { + int size = listModel.size(); + if (size == 0) + { + return null; + } + + List memberSpecifications = new ArrayList(size); + for (int index = 0; index < size; index++) + { + MyMemberSpecificationWrapper wrapper = + (MyMemberSpecificationWrapper)listModel.get(index); + + if (wrapper.isField == isField) + { + memberSpecifications.add(wrapper.memberSpecification); + } + } + + return memberSpecifications; + } + + + /** + * This ListCellRenderer renders MemberSpecification objects. + */ + private static class MyListCellRenderer implements ListCellRenderer + { + private final JLabel label = new JLabel(); + + + // Implementations for ListCellRenderer. + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + MyMemberSpecificationWrapper wrapper = (MyMemberSpecificationWrapper)value; + + MemberSpecification option = wrapper.memberSpecification; + String name = option.name; + String descriptor = option.descriptor; + + label.setText(wrapper.isField ? + (descriptor == null ? name == null ? + "" : + "***" + ' ' + name : + ClassUtil.externalFullFieldDescription(0, + name == null ? "*" : name, + descriptor)) : + (descriptor == null ? name == null ? + "" : + "***" + ' ' + name + "(...)" : + ClassUtil.externalFullMethodDescription(ClassConstants.INTERNAL_METHOD_NAME_INIT, + 0, + name == null ? "*" : name, + descriptor))); + + if (isSelected) + { + label.setBackground(list.getSelectionBackground()); + label.setForeground(list.getSelectionForeground()); + } + else + { + label.setBackground(list.getBackground()); + label.setForeground(list.getForeground()); + } + + label.setOpaque(true); + + return label; + } + } + + + /** + * 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 MyMemberSpecificationWrapper + { + public MemberSpecification memberSpecification; + public final boolean isField; + + public MyMemberSpecificationWrapper(MemberSpecification memberSpecification, + boolean isField) + { + this.memberSpecification = memberSpecification; + this.isField = isField; + } + } +} diff --git a/src/proguard/gui/MessageDialogRunnable.java b/src/proguard/gui/MessageDialogRunnable.java new file mode 100644 index 000000000..6c2152cb2 --- /dev/null +++ b/src/proguard/gui/MessageDialogRunnable.java @@ -0,0 +1,90 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.gui; + +import javax.swing.*; +import java.awt.*; +import java.lang.reflect.InvocationTargetException; + + +/** + * This Runnable can show a message dialog. + * + * @author Eric Lafortune + */ +final class MessageDialogRunnable implements Runnable +{ + private final Component parentComponent; + private final Object message; + private final String title; + private final int messageType; + + + /** + * Creates a new MessageDialogRunnable object. + * @see JOptionPane#showMessageDialog(Component, Object, String, int) + */ + public static void showMessageDialog(Component parentComponent, + Object message, + String title, + int messageType) + { + try + { + SwingUtil.invokeAndWait(new MessageDialogRunnable(parentComponent, + message, + title, + messageType)); + } + catch (Exception e) + { + // Nothing. + } + } + + + /** + * Creates a new MessageDialogRunnable object. + * @see JOptionPane#showMessageDialog(Component, Object, String, int) + */ + public MessageDialogRunnable(Component parentComponent, + Object message, + String title, + int messageType) + { + this.parentComponent = parentComponent; + this.message = message; + this.title = title; + this.messageType = messageType; + } + + + + // Implementation for Runnable. + + public void run() + { + JOptionPane.showMessageDialog(parentComponent, + message, + title, + messageType); + } +} diff --git a/src/proguard/gui/OptimizationsDialog.java b/src/proguard/gui/OptimizationsDialog.java new file mode 100644 index 000000000..0af0979c1 --- /dev/null +++ b/src/proguard/gui/OptimizationsDialog.java @@ -0,0 +1,251 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.gui; + +import proguard.optimize.Optimizer; +import proguard.util.*; + +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; + +/** + * This JDialog allows the user to enter a String. + * + * @author Eric Lafortune + */ +final class OptimizationsDialog extends JDialog +{ + /** + * Return value if the dialog is canceled (with the Cancel button or by + * closing the dialog window). + */ + public static final int CANCEL_OPTION = 1; + + /** + * Return value if the dialog is approved (with the Ok button). + */ + public static final int APPROVE_OPTION = 0; + + + private final JCheckBox[] optimizationCheckBoxes = new JCheckBox[Optimizer.OPTIMIZATION_NAMES.length]; + + private int returnValue; + + + public OptimizationsDialog(JFrame owner) + { + super(owner, msg("selectOptimizations"), true); + setResizable(true); + + // Create some constraints that can be reused. + GridBagConstraints constraintsLast = new GridBagConstraints(); + constraintsLast.gridwidth = GridBagConstraints.REMAINDER; + constraintsLast.anchor = GridBagConstraints.WEST; + constraintsLast.insets = new Insets(1, 2, 1, 2); + + GridBagConstraints constraintsLastStretch = new GridBagConstraints(); + constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER; + constraintsLastStretch.fill = GridBagConstraints.HORIZONTAL; + constraintsLastStretch.weightx = 1.0; + constraintsLastStretch.anchor = GridBagConstraints.WEST; + constraintsLastStretch.insets = constraintsLast.insets; + + GridBagConstraints panelConstraints = new GridBagConstraints(); + panelConstraints.gridwidth = GridBagConstraints.REMAINDER; + panelConstraints.fill = GridBagConstraints.HORIZONTAL; + panelConstraints.weightx = 1.0; + panelConstraints.weighty = 0.0; + panelConstraints.anchor = GridBagConstraints.NORTHWEST; + panelConstraints.insets = constraintsLast.insets; + + GridBagConstraints selectButtonConstraints = new GridBagConstraints(); + selectButtonConstraints.weighty = 1.0; + selectButtonConstraints.anchor = GridBagConstraints.SOUTHWEST; + selectButtonConstraints.insets = new Insets(4, 4, 8, 4); + + GridBagConstraints okButtonConstraints = new GridBagConstraints(); + okButtonConstraints.weightx = 1.0; + okButtonConstraints.weighty = 1.0; + okButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; + okButtonConstraints.insets = selectButtonConstraints.insets; + + GridBagConstraints cancelButtonConstraints = new GridBagConstraints(); + cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER; + cancelButtonConstraints.weighty = 1.0; + cancelButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; + cancelButtonConstraints.insets = selectButtonConstraints.insets; + + GridBagLayout layout = new GridBagLayout(); + + Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); + + // Create the optimizations panel. + JPanel optimizationsPanel = new JPanel(layout); + JPanel optimizationSubpanel = null; + String lastOptimizationPrefix = null; + + for (int index = 0; index < Optimizer.OPTIMIZATION_NAMES.length; index++) + { + String optimizationName = Optimizer.OPTIMIZATION_NAMES[index]; + + String optimizationPrefix = optimizationName.substring(0, optimizationName.indexOf('/')); + + if (optimizationSubpanel == null || !optimizationPrefix.equals(lastOptimizationPrefix)) + { + // Create a new keep subpanel and add it. + optimizationSubpanel = new JPanel(layout); + optimizationSubpanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg(optimizationPrefix))); + optimizationsPanel.add(optimizationSubpanel, panelConstraints); + + lastOptimizationPrefix = optimizationPrefix; + } + + JCheckBox optimizationCheckBox = new JCheckBox(optimizationName); + optimizationCheckBoxes[index] = optimizationCheckBox; + + optimizationSubpanel.add(tip(optimizationCheckBox, optimizationName.replace('/', '_')+"Tip"), constraintsLastStretch); + } + + // Create the Select All button. + JButton selectAllButton = new JButton(msg("selectAll")); + selectAllButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + for (int index = 0; index < optimizationCheckBoxes.length; index++) + { + optimizationCheckBoxes[index].setSelected(true); + } + } + }); + + // Create the Select All button. + JButton selectNoneButton = new JButton(msg("selectNone")); + selectNoneButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + for (int index = 0; index < optimizationCheckBoxes.length; index++) + { + optimizationCheckBoxes[index].setSelected(false); + } + } + }); + + // Create the Ok button. + JButton okButton = new JButton(msg("ok")); + okButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + returnValue = APPROVE_OPTION; + hide(); + } + }); + + // Create the Cancel button. + JButton cancelButton = new JButton(msg("cancel")); + cancelButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + hide(); + } + }); + + // Add all panels to the main panel. + optimizationsPanel.add(selectAllButton, selectButtonConstraints); + optimizationsPanel.add(selectNoneButton, selectButtonConstraints); + optimizationsPanel.add(okButton, okButtonConstraints); + optimizationsPanel.add(cancelButton, cancelButtonConstraints); + + getContentPane().add(new JScrollPane(optimizationsPanel)); + } + + + /** + * Sets the initial optimization filter to be used by the dialog. + */ + public void setFilter(String optimizations) + { + StringMatcher filter = optimizations != null && optimizations.length() > 0 ? + new ListParser(new NameParser()).parse(optimizations) : + new FixedStringMatcher(""); + + for (int index = 0; index < Optimizer.OPTIMIZATION_NAMES.length; index++) + { + optimizationCheckBoxes[index].setSelected(filter.matches(Optimizer.OPTIMIZATION_NAMES[index])); + } + } + + + /** + * Returns the optimization filter composed from the settings in the dialog. + */ + public String getFilter() + { + return new FilterBuilder(optimizationCheckBoxes, '/').buildFilter(); + } + + + /** + * Shows this dialog. This method only returns when the dialog is closed. + * + * @return CANCEL_OPTION or APPROVE_OPTION, + * depending on the choice of the user. + */ + public int showDialog() + { + returnValue = CANCEL_OPTION; + + // Open the dialog in the right place, then wait for it to be closed, + // one way or another. + pack(); + setLocationRelativeTo(getOwner()); + show(); + + return returnValue; + } + + + /** + * Attaches the tool tip from the GUI resources that corresponds to the + * given key, to the given component. + */ + private static JComponent tip(JComponent component, String messageKey) + { + component.setToolTipText(msg(messageKey)); + + return component; + } + + + /** + * Returns the message from the GUI resources that corresponds to the given + * key. + */ + private static String msg(String messageKey) + { + return GUIResources.getMessage(messageKey); + } +} \ No newline at end of file diff --git a/src/proguard/gui/ProGuardGUI.java b/src/proguard/gui/ProGuardGUI.java new file mode 100644 index 000000000..6b08aa8bc --- /dev/null +++ b/src/proguard/gui/ProGuardGUI.java @@ -0,0 +1,1771 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 proguard.classfile.util.ClassUtil; +import proguard.gui.splash.*; +import proguard.util.ListUtil; + +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.URL; +import java.util.*; +import java.util.List; + + +/** + * GUI for configuring and executing ProGuard and ReTrace. + * + * @author Eric Lafortune + */ +public class ProGuardGUI extends JFrame +{ + private static final String NO_SPLASH_OPTION = "-nosplash"; + + private static final String TITLE_IMAGE_FILE = "vtitle.png"; + private static final String BOILERPLATE_CONFIGURATION = "boilerplate.pro"; + private static final String DEFAULT_CONFIGURATION = "default.pro"; + + private static final String OPTIMIZATIONS_DEFAULT = "*"; + private static final String KEEP_ATTRIBUTE_DEFAULT = "Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod"; + private static final String SOURCE_FILE_ATTRIBUTE_DEFAULT = "SourceFile"; + private static final String ADAPT_RESOURCE_FILE_NAMES_DEFAULT = "**.properties"; + 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 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 KeepClassSpecification[] boilerplateKeep; + private final JCheckBox[] boilerplateKeepCheckBoxes; + private final JTextField[] boilerplateKeepTextFields; + + private final KeepSpecificationsPanel additionalKeepPanel = new KeepSpecificationsPanel(this, true, false, false, false, false); + + private KeepClassSpecification[] 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 JCheckBox mergeInterfacesAggressivelyCheckBox = new JCheckBox(msg("mergeInterfacesAggressively")); + private final JLabel optimizationsLabel = new JLabel(msg("optimizations")); + private final JLabel optimizationPassesLabel = new JLabel(msg("optimizationPasses")); + + private final JSpinner optimizationPassesSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 9, 1)); + + private final 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 classObfuscationDictionaryCheckBox = new JCheckBox(msg("classObfuscationDictionary")); + private final JCheckBox packageObfuscationDictionaryCheckBox = new JCheckBox(msg("packageObfuscationDictionary")); + private final JCheckBox overloadAggressivelyCheckBox = new JCheckBox(msg("overloadAggressively")); + private final JCheckBox useUniqueClassMemberNamesCheckBox = new JCheckBox(msg("useUniqueClassMemberNames")); + private final JCheckBox useMixedCaseClassNamesCheckBox = new JCheckBox(msg("useMixedCaseClassNames")); + private final JCheckBox keepPackageNamesCheckBox = new JCheckBox(msg("keepPackageNames")); + private final JCheckBox flattenPackageHierarchyCheckBox = new JCheckBox(msg("flattenPackageHierarchy")); + private final JCheckBox repackageClassesCheckBox = new JCheckBox(msg("repackageClasses")); + private final JCheckBox keepAttributesCheckBox = new JCheckBox(msg("keepAttributes")); + private final JCheckBox keepParameterNamesCheckBox = new JCheckBox(msg("keepParameterNames")); + private final JCheckBox newSourceFileAttributeCheckBox = new JCheckBox(msg("renameSourceFileAttribute")); + private final JCheckBox adaptClassStringsCheckBox = new JCheckBox(msg("adaptClassStrings")); + private final JCheckBox adaptResourceFileNamesCheckBox = new JCheckBox(msg("adaptResourceFileNames")); + private final JCheckBox adaptResourceFileContentsCheckBox = new JCheckBox(msg("adaptResourceFileContents")); + + 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 noteCheckBox = new JCheckBox(msg("note")); + private final JCheckBox warnCheckBox = new JCheckBox(msg("warn")); + private final JCheckBox ignoreWarningsCheckBox = new JCheckBox(msg("ignoreWarnings")); + private final JCheckBox skipNonPublicLibraryClassesCheckBox = new JCheckBox(msg("skipNonPublicLibraryClasses")); + private final JCheckBox skipNonPublicLibraryClassMembersCheckBox = new JCheckBox(msg("skipNonPublicLibraryClassMembers")); + private final JCheckBox keepDirectoriesCheckBox = new JCheckBox(msg("keepDirectories")); + private final JCheckBox forceProcessingCheckBox = new JCheckBox(msg("forceProcessing")); + private final JCheckBox printSeedsCheckBox = new JCheckBox(msg("printSeeds")); + private final JCheckBox printConfigurationCheckBox = new JCheckBox(msg("printConfiguration")); + private final JCheckBox dumpCheckBox = new JCheckBox(msg("dump")); + + private final JTextField printUsageTextField = new JTextField(40); + private final JTextField optimizationsTextField = new JTextField(40); + private final JTextField printMappingTextField = new JTextField(40); + private final JTextField applyMappingTextField = new JTextField(40); + private final JTextField obfuscationDictionaryTextField = new JTextField(40); + private final JTextField classObfuscationDictionaryTextField = new JTextField(40); + private final JTextField packageObfuscationDictionaryTextField = new JTextField(40); + private final JTextField keepPackageNamesTextField = new JTextField(40); + private final JTextField flattenPackageHierarchyTextField = new JTextField(40); + private final JTextField repackageClassesTextField = new JTextField(40); + private final JTextField keepAttributesTextField = new JTextField(40); + private final JTextField newSourceFileAttributeTextField = new JTextField(40); + private final JTextField adaptClassStringsTextField = new JTextField(40); + private final JTextField adaptResourceFileNamesTextField = new JTextField(40); + private final JTextField adaptResourceFileContentsTextField = new JTextField(40); + private final JTextField noteTextField = new JTextField(40); + private final JTextField warnTextField = new JTextField(40); + private final JTextField keepDirectoriesTextField = new JTextField(40); + private final JTextField printSeedsTextField = new JTextField(40); + private final JTextField printConfigurationTextField = new JTextField(40); + private final JTextField dumpTextField = new JTextField(40); + + 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); + + + /** + * Creates a new ProGuardGUI. + */ + public ProGuardGUI() + { + setTitle("ProGuard"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + // Create some constraints that can be reused. + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.WEST; + constraints.insets = new Insets(0, 4, 0, 4); + + 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 splashPanelConstraints = new GridBagConstraints(); + splashPanelConstraints.gridwidth = GridBagConstraints.REMAINDER; + splashPanelConstraints.fill = GridBagConstraints.BOTH; + splashPanelConstraints.weightx = 1.0; + splashPanelConstraints.weighty = 0.02; + splashPanelConstraints.anchor = GridBagConstraints.NORTHWEST; + //splashPanelConstraints.insets = constraints.insets; + + GridBagConstraints welcomePaneConstraints = new GridBagConstraints(); + welcomePaneConstraints.gridwidth = GridBagConstraints.REMAINDER; + welcomePaneConstraints.fill = GridBagConstraints.NONE; + welcomePaneConstraints.weightx = 1.0; + welcomePaneConstraints.weighty = 0.01; + welcomePaneConstraints.anchor = GridBagConstraints.CENTER;//NORTHWEST; + welcomePaneConstraints.insets = new Insets(20, 40, 20, 40); + + GridBagConstraints panelConstraints = new GridBagConstraints(); + panelConstraints.gridwidth = GridBagConstraints.REMAINDER; + panelConstraints.fill = GridBagConstraints.HORIZONTAL; + panelConstraints.weightx = 1.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 glueConstraints = new GridBagConstraints(); + glueConstraints.fill = GridBagConstraints.BOTH; + glueConstraints.weightx = 0.01; + glueConstraints.weighty = 0.01; + glueConstraints.anchor = GridBagConstraints.NORTHWEST; + glueConstraints.insets = constraints.insets; + + GridBagConstraints bottomButtonConstraints = new GridBagConstraints(); + bottomButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; + bottomButtonConstraints.insets = new Insets(2, 2, 4, 6); + bottomButtonConstraints.ipadx = 10; + bottomButtonConstraints.ipady = 2; + + GridBagConstraints lastBottomButtonConstraints = new GridBagConstraints(); + lastBottomButtonConstraints.gridwidth = GridBagConstraints.REMAINDER; + lastBottomButtonConstraints.anchor = GridBagConstraints.SOUTHEAST; + lastBottomButtonConstraints.insets = bottomButtonConstraints.insets; + lastBottomButtonConstraints.ipadx = bottomButtonConstraints.ipadx; + lastBottomButtonConstraints.ipady = bottomButtonConstraints.ipady; + + // Leave room for a growBox on Mac OS X. + if (System.getProperty("os.name").toLowerCase().startsWith("mac os x")) + { + lastBottomButtonConstraints.insets = new Insets(2, 2, 4, 6 + 16); + } + + GridBagLayout layout = new GridBagLayout(); + + configurationChooser.addChoosableFileFilter( + new ExtensionFileFilter(msg("proExtension"), new String[] { ".pro" })); + + // Create the opening panel. + 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 ConstantInt(160), + 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(1), + new CompositeSprite(new Sprite[] + { + new TextSprite(new ConstantString(msg("shrinking")), + new LinearInt(1000, 60, new SmoothTiming(1000, 2000)), + new ConstantInt(70)), + new TextSprite(new ConstantString(msg("optimization")), + new LinearInt(1000, 400, new SmoothTiming(1500, 2500)), + new ConstantInt(60)), + new TextSprite(new ConstantString(msg("obfuscation")), + 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 FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 30)), + new TextSprite(new TypeWriterString(msg("developed"), new LinearTiming(3500, 5500)), + new ConstantInt(250), + new ConstantInt(200))), + })))), + }); + splashPanel = new SplashPanel(splash, 0.5, 5500L); + splashPanel.setPreferredSize(new Dimension(0, 200)); + + JEditorPane welcomePane = new JEditorPane("text/html", msg("proGuardInfo")); + welcomePane.setPreferredSize(new Dimension(640, 350)); + // The constant HONOR_DISPLAY_PROPERTIES isn't present yet in JDK 1.4. + //welcomePane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); + welcomePane.putClientProperty("JEditorPane.honorDisplayProperties", Boolean.TRUE); + welcomePane.setOpaque(false); + welcomePane.setEditable(false); + welcomePane.setBorder(new EmptyBorder(20, 20, 20, 20)); + addBorder(welcomePane, "welcome"); + + JPanel proGuardPanel = new JPanel(layout); + proGuardPanel.add(splashPanel, splashPanelConstraints); + proGuardPanel.add(welcomePane, welcomePaneConstraints); + + // Create the input panel. + // 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("moveToLibraries", "moveToLibrariesTip", libraryPanel); + libraryPanel.addCopyToPanelButton("moveToProgram", "moveToProgramTip", programPanel); + + // Collect all buttons of these panels and make sure they are equally + // sized. + List panelButtons = new ArrayList(); + panelButtons.addAll(programPanel.getButtons()); + panelButtons.addAll(libraryPanel.getButtons()); + setCommonPreferredSize(panelButtons); + + addBorder(programPanel, "programJars" ); + addBorder(libraryPanel, "libraryJars" ); + + JPanel inputOutputPanel = new JPanel(layout); + inputOutputPanel.add(tip(programPanel, "programJarsTip"), stretchPanelConstraints); + inputOutputPanel.add(tip(libraryPanel, "libraryJarsTip"), stretchPanelConstraints); + + // Load the boiler plate options. + loadBoilerplateConfiguration(); + + // Create the boiler plate keep panels. + boilerplateKeepCheckBoxes = new JCheckBox[boilerplateKeep.length]; + boilerplateKeepTextFields = new JTextField[boilerplateKeep.length]; + + JButton printUsageBrowseButton = createBrowseButton(printUsageTextField, + msg("selectUsageFile")); + + JPanel shrinkingOptionsPanel = new JPanel(layout); + addBorder(shrinkingOptionsPanel, "options"); + + 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(extractClassSpecifications(boilerplateKeep), + shrinkingPanel, + boilerplateKeepCheckBoxes, + boilerplateKeepTextFields); + + addBorder(additionalKeepPanel, "keepAdditional"); + shrinkingPanel.add(tip(additionalKeepPanel, "keepAdditionalTip"), stretchPanelConstraints); + + // Create the boiler plate keep names panels. + boilerplateKeepNamesCheckBoxes = new JCheckBox[boilerplateKeepNames.length]; + boilerplateKeepNamesTextFields = new JTextField[boilerplateKeepNames.length]; + + JButton printMappingBrowseButton = createBrowseButton(printMappingTextField, + msg("selectPrintMappingFile")); + JButton applyMappingBrowseButton = createBrowseButton(applyMappingTextField, + msg("selectApplyMappingFile")); + JButton obfucationDictionaryBrowseButton = createBrowseButton(obfuscationDictionaryTextField, + msg("selectObfuscationDictionaryFile")); + JButton classObfucationDictionaryBrowseButton = createBrowseButton(classObfuscationDictionaryTextField, + msg("selectObfuscationDictionaryFile")); + JButton packageObfucationDictionaryBrowseButton = createBrowseButton(packageObfuscationDictionaryTextField, + msg("selectObfuscationDictionaryFile")); + + JPanel obfuscationOptionsPanel = new JPanel(layout); + addBorder(obfuscationOptionsPanel, "options"); + + 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(classObfuscationDictionaryCheckBox, "classObfuscationDictionaryTip"), constraints); + obfuscationOptionsPanel.add(tip(classObfuscationDictionaryTextField, "inputFileTip"), constraintsStretch); + obfuscationOptionsPanel.add(tip(classObfucationDictionaryBrowseButton, "selectObfuscationDictionaryFile"), constraintsLast); + obfuscationOptionsPanel.add(tip(packageObfuscationDictionaryCheckBox, "packageObfuscationDictionaryTip"), constraints); + obfuscationOptionsPanel.add(tip(packageObfuscationDictionaryTextField, "inputFileTip"), constraintsStretch); + obfuscationOptionsPanel.add(tip(packageObfucationDictionaryBrowseButton, "selectObfuscationDictionaryFile"), constraintsLast); + obfuscationOptionsPanel.add(tip(overloadAggressivelyCheckBox, "overloadAggressivelyTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(useUniqueClassMemberNamesCheckBox, "useUniqueClassMemberNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(useMixedCaseClassNamesCheckBox, "useMixedCaseClassNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(keepPackageNamesCheckBox, "keepPackageNamesTip"), constraints); + obfuscationOptionsPanel.add(tip(keepPackageNamesTextField, "packageNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(flattenPackageHierarchyCheckBox, "flattenPackageHierarchyTip"), constraints); + obfuscationOptionsPanel.add(tip(flattenPackageHierarchyTextField, "packageTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(repackageClassesCheckBox, "repackageClassesTip"), constraints); + obfuscationOptionsPanel.add(tip(repackageClassesTextField, "packageTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(keepAttributesCheckBox, "keepAttributesTip"), constraints); + obfuscationOptionsPanel.add(tip(keepAttributesTextField, "attributesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(keepParameterNamesCheckBox, "keepParameterNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(newSourceFileAttributeCheckBox, "renameSourceFileAttributeTip"), constraints); + obfuscationOptionsPanel.add(tip(newSourceFileAttributeTextField, "sourceFileAttributeTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(adaptClassStringsCheckBox, "adaptClassStringsTip"), constraints); + obfuscationOptionsPanel.add(tip(adaptClassStringsTextField, "classNamesTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(adaptResourceFileNamesCheckBox, "adaptResourceFileNamesTip"), constraints); + obfuscationOptionsPanel.add(tip(adaptResourceFileNamesTextField, "fileNameFilterTip"), constraintsLastStretch); + obfuscationOptionsPanel.add(tip(adaptResourceFileContentsCheckBox, "adaptResourceFileContentsTip"), constraints); + obfuscationOptionsPanel.add(tip(adaptResourceFileContentsTextField, "fileNameFilterTip"), constraintsLastStretch); + + JPanel obfuscationPanel = new JPanel(layout); + + obfuscationPanel.add(obfuscationOptionsPanel, panelConstraints); + addClassSpecifications(extractClassSpecifications(boilerplateKeepNames), + obfuscationPanel, + boilerplateKeepNamesCheckBoxes, + boilerplateKeepNamesTextFields); + + addBorder(additionalKeepNamesPanel, "keepNamesAdditional"); + obfuscationPanel.add(tip(additionalKeepNamesPanel, "keepNamesAdditionalTip"), stretchPanelConstraints); + + // Create the boiler plate "no side effect methods" panels. + boilerplateNoSideEffectMethodCheckBoxes = new JCheckBox[boilerplateNoSideEffectMethods.length]; + + JPanel optimizationOptionsPanel = new JPanel(layout); + addBorder(optimizationOptionsPanel, "options"); + + JButton optimizationsButton = + createOptimizationsButton(optimizationsTextField); + + optimizationOptionsPanel.add(tip(optimizeCheckBox, "optimizeTip"), constraintsLastStretch); + optimizationOptionsPanel.add(tip(allowAccessModificationCheckBox, "allowAccessModificationTip"), constraintsLastStretch); + optimizationOptionsPanel.add(tip(mergeInterfacesAggressivelyCheckBox, "mergeInterfacesAggressivelyTip"), constraintsLastStretch); + optimizationOptionsPanel.add(tip(optimizationsLabel, "optimizationsTip"), constraints); + optimizationOptionsPanel.add(tip(optimizationsTextField, "optimizationsFilterTip"), constraintsStretch); + optimizationOptionsPanel.add(tip(optimizationsButton, "optimizationsSelectTip"), constraintsLast); + optimizationOptionsPanel.add(tip(optimizationPassesLabel, "optimizationPassesTip"), constraints); + optimizationOptionsPanel.add(tip(optimizationPassesSpinner, "optimizationPassesTip"), constraintsLast); + + JPanel optimizationPanel = new JPanel(layout); + + optimizationPanel.add(optimizationOptionsPanel, panelConstraints); + addClassSpecifications(boilerplateNoSideEffectMethods, + optimizationPanel, + boilerplateNoSideEffectMethodCheckBoxes, + null); + + addBorder(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditional"); + optimizationPanel.add(tip(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditionalTip"), stretchPanelConstraints); + + // Create the options panel. + 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(tip(verboseCheckBox, "verboseTip"), constraintsLastStretch); + consistencyPanel.add(tip(noteCheckBox, "noteTip"), constraints); + consistencyPanel.add(tip(noteTextField, "noteFilterTip"), constraintsLastStretch); + consistencyPanel.add(tip(warnCheckBox, "warnTip"), constraints); + consistencyPanel.add(tip(warnTextField, "warnFilterTip"), constraintsLastStretch); + consistencyPanel.add(tip(ignoreWarningsCheckBox, "ignoreWarningsTip"), constraintsLastStretch); + consistencyPanel.add(tip(skipNonPublicLibraryClassesCheckBox, "skipNonPublicLibraryClassesTip"), constraintsLastStretch); + consistencyPanel.add(tip(skipNonPublicLibraryClassMembersCheckBox, "skipNonPublicLibraryClassMembersTip"), constraintsLastStretch); + consistencyPanel.add(tip(keepDirectoriesCheckBox, "keepDirectoriesTip"), constraints); + consistencyPanel.add(tip(keepDirectoriesTextField, "directoriesTip"), constraintsLastStretch); + consistencyPanel.add(tip(forceProcessingCheckBox, "forceProcessingTip"), constraintsLastStretch); + consistencyPanel.add(tip(printSeedsCheckBox, "printSeedsTip"), constraints); + consistencyPanel.add(tip(printSeedsTextField, "outputFileTip"), constraintsStretch); + 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 + // same horizontal position. + setCommonPreferredSize(Arrays.asList(new JComponent[] { + printMappingCheckBox, + applyMappingCheckBox, + flattenPackageHierarchyCheckBox, + repackageClassesCheckBox, + newSourceFileAttributeCheckBox, + })); + + JPanel optionsPanel = new JPanel(layout); + + optionsPanel.add(preverificationOptionsPanel, panelConstraints); + optionsPanel.add(consistencyPanel, panelConstraints); + + addBorder(whyAreYouKeepingPanel, "whyAreYouKeeping"); + optionsPanel.add(tip(whyAreYouKeepingPanel, "whyAreYouKeepingTip"), stretchPanelConstraints); + + // Create the process panel. + consoleTextArea.setOpaque(false); + consoleTextArea.setEditable(false); + consoleTextArea.setLineWrap(false); + consoleTextArea.setWrapStyleWord(false); + JScrollPane consoleScrollPane = new JScrollPane(consoleTextArea); + consoleScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1)); + addBorder(consoleScrollPane, "processingConsole"); + + JPanel processPanel = new JPanel(layout); + processPanel.add(consoleScrollPane, stretchPanelConstraints); + + // Create the load, save, and process buttons. + JButton loadButton = new JButton(msg("loadConfiguration")); + loadButton.addActionListener(new MyLoadConfigurationActionListener()); + + JButton viewButton = new JButton(msg("viewConfiguration")); + viewButton.addActionListener(new MyViewConfigurationActionListener()); + + JButton saveButton = new JButton(msg("saveConfiguration")); + saveButton.addActionListener(new MySaveConfigurationActionListener()); + + JButton processButton = new JButton(msg("process")); + processButton.addActionListener(new MyProcessActionListener()); + + // Create the ReTrace panel. + JPanel reTraceSettingsPanel = new JPanel(layout); + addBorder(reTraceSettingsPanel, "reTraceSettings"); + + JButton reTraceMappingBrowseButton = createBrowseButton(reTraceMappingTextField, + msg("selectApplyMappingFile")); + + JLabel reTraceMappingLabel = new JLabel(msg("mappingFile")); + reTraceMappingLabel.setForeground(reTraceVerboseCheckBox.getForeground()); + + 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); + stackTraceTextArea.setLineWrap(false); + stackTraceTextArea.setWrapStyleWord(true); + JScrollPane stackTraceScrollPane = new JScrollPane(stackTraceTextArea); + addBorder(stackTraceScrollPane, "obfuscatedStackTrace"); + + reTraceTextArea.setOpaque(false); + reTraceTextArea.setEditable(false); + reTraceTextArea.setLineWrap(true); + reTraceTextArea.setWrapStyleWord(true); + JScrollPane reTraceScrollPane = new JScrollPane(reTraceTextArea); + reTraceScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1)); + addBorder(reTraceScrollPane, "deobfuscatedStackTrace"); + + JPanel reTracePanel = new JPanel(layout); + reTracePanel.add(reTraceSettingsPanel, panelConstraints); + reTracePanel.add(tip(stackTraceScrollPane, "obfuscatedStackTraceTip"), panelConstraints); + reTracePanel.add(reTraceScrollPane, stretchPanelConstraints); + + // Create the load button. + JButton loadStackTraceButton = new JButton(msg("loadStackTrace")); + loadStackTraceButton.addActionListener(new MyLoadStackTraceActionListener()); + + JButton reTraceButton = new JButton(msg("reTrace")); + reTraceButton.addActionListener(new MyReTraceActionListener()); + + // Create the main tabbed pane. + TabbedPane tabs = new TabbedPane(); + tabs.add(msg("proGuardTab"), proGuardPanel); + tabs.add(msg("inputOutputTab"), inputOutputPanel); + tabs.add(msg("shrinkingTab"), shrinkingPanel); + tabs.add(msg("obfuscationTab"), obfuscationPanel); + tabs.add(msg("optimizationTab"), optimizationPanel); + tabs.add(msg("informationTab"), optionsPanel); + tabs.add(msg("processTab"), processPanel); + tabs.add(msg("reTraceTab"), reTracePanel); + tabs.addImage(Toolkit.getDefaultToolkit().getImage( + this.getClass().getResource(TITLE_IMAGE_FILE))); + + // Add the bottom buttons to each panel. + 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); + inputOutputPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + shrinkingPanel .add(Box.createGlue(), glueConstraints); + shrinkingPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + shrinkingPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + obfuscationPanel .add(Box.createGlue(), glueConstraints); + obfuscationPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + obfuscationPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + optimizationPanel .add(Box.createGlue(), glueConstraints); + optimizationPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + optimizationPanel .add(createNextButton(tabs), lastBottomButtonConstraints); + + optionsPanel .add(Box.createGlue(), glueConstraints); + optionsPanel .add(createPreviousButton(tabs), bottomButtonConstraints); + optionsPanel .add(createNextButton(tabs), 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(tip(loadStackTraceButton, "loadStackTraceTip"), bottomButtonConstraints); + reTracePanel .add(tip(reTraceButton, "reTraceTip"), lastBottomButtonConstraints); + + // Add the main tabs to the frame. + getContentPane().add(tabs); + + // Pack the entire GUI before setting some default values. + pack(); + + // Initialize the GUI settings to reasonable defaults. + loadConfiguration(this.getClass().getResource(DEFAULT_CONFIGURATION)); + } + + + public void startSplash() + { + splashPanel.start(); + } + + + public void skipSplash() + { + splashPanel.stop(); + } + + + /** + * Loads the boilerplate keep class options from the boilerplate file + * into the boilerplate array. + */ + private void loadBoilerplateConfiguration() + { + try + { + // Parse the boilerplate configuration file. + ConfigurationParser parser = new ConfigurationParser( + this.getClass().getResource(BOILERPLATE_CONFIGURATION), + System.getProperties()); + + Configuration configuration = new Configuration(); + + try + { + parser.parse(configuration); + + // We're interested in the keep options. + boilerplateKeep = + extractKeepSpecifications(configuration.keep, false, false); + + // 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()]; + configuration.assumeNoSideEffects.toArray(boilerplateNoSideEffectMethods); + } + finally + { + parser.close(); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + + /** + * Returns an array containing the ClassSpecifications instances with + * matching flags. + */ + private KeepClassSpecification[] extractKeepSpecifications(List keepSpecifications, + boolean allowShrinking, + boolean allowObfuscation) + { + List matches = new ArrayList(); + + for (int index = 0; index < keepSpecifications.size(); index++) + { + KeepClassSpecification keepClassSpecification = (KeepClassSpecification)keepSpecifications.get(index); + if (keepClassSpecification.allowShrinking == allowShrinking && + keepClassSpecification.allowObfuscation == allowObfuscation) + { + matches.add(keepClassSpecification); + } + } + + KeepClassSpecification[] matchingKeepClassSpecifications = new KeepClassSpecification[matches.size()]; + matches.toArray(matchingKeepClassSpecifications); + + return matchingKeepClassSpecifications; + } + + + /** + * Returns an array containing the ClassSpecification instances of the + * given array of KeepClassSpecification instances. + */ + private ClassSpecification[] extractClassSpecifications(KeepClassSpecification[] keepClassSpecifications) + { + ClassSpecification[] classSpecifications = new ClassSpecification[keepClassSpecifications.length]; + + for (int index = 0; index < classSpecifications.length; index++) + { + classSpecifications[index] = keepClassSpecifications[index]; + } + + return classSpecifications; + } + + + /** + * Creates a panel with the given boiler plate class specifications. + */ + private void addClassSpecifications(ClassSpecification[] boilerplateClassSpecifications, + JPanel classSpecificationsPanel, + JCheckBox[] boilerplateCheckBoxes, + JTextField[] boilerplateTextFields) + { + // Create some constraints that can be reused. + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.WEST; + constraints.insets = new Insets(0, 4, 0, 4); + + 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.anchor = GridBagConstraints.NORTHWEST; + panelConstraints.insets = constraints.insets; + + GridBagLayout layout = new GridBagLayout(); + + String lastPanelName = null; + JPanel keepSubpanel = null; + for (int index = 0; index < boilerplateClassSpecifications.length; index++) + { + // The panel structure is derived from the comments. + 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); + keepSubpanel.setBorder(BorderFactory.createTitledBorder(BORDER, panelName)); + classSpecificationsPanel.add(keepSubpanel, panelConstraints); + + lastPanelName = panelName; + } + + // Add the check box to the subpanel. + JCheckBox boilerplateCheckBox = new JCheckBox(optionName); + boilerplateCheckBox.setToolTipText(toolTip); + boilerplateCheckBoxes[index] = boilerplateCheckBox; + keepSubpanel.add(boilerplateCheckBox, + boilerplateTextFields != null ? + constraints : + constraintsLastStretch); + + if (boilerplateTextFields != null) + { + // Add the text field to the subpanel. + boilerplateTextFields[index] = new JTextField(40); + keepSubpanel.add(tip(boilerplateTextFields[index], "classNamesTip"), constraintsLastStretch); + } + } + } + + + /** + * Adds a standard border with the title that corresponds to the given key + * in the GUI resources. + */ + private void addBorder(JComponent component, String titleKey) + { + Border oldBorder = component.getBorder(); + Border newBorder = BorderFactory.createTitledBorder(BORDER, msg(titleKey)); + + component.setBorder(oldBorder == null ? + newBorder : + new CompoundBorder(newBorder, oldBorder)); + } + + + /** + * Creates a Previous button for the given tabbed pane. + */ + private JButton createPreviousButton(final TabbedPane tabbedPane) + { + JButton browseButton = new JButton(msg("previous")); + browseButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + tabbedPane.previous(); + } + }); + + return browseButton; + } + + + /** + * Creates a Next button for the given tabbed pane. + */ + private JButton createNextButton(final TabbedPane tabbedPane) + { + JButton browseButton = new JButton(msg("next")); + browseButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + tabbedPane.next(); + } + }); + + return browseButton; + } + + + /** + * Creates a browse button that opens a file browser for the given text field. + */ + private JButton createBrowseButton(final JTextField textField, + final String title) + { + JButton browseButton = new JButton(msg("browse")); + browseButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // Update the file chooser. + fileChooser.setDialogTitle(title); + fileChooser.setSelectedFile(new File(textField.getText())); + + int returnVal = fileChooser.showDialog(ProGuardGUI.this, msg("ok")); + if (returnVal == JFileChooser.APPROVE_OPTION) + { + // Update the text field. + textField.setText(fileChooser.getSelectedFile().getPath()); + } + } + }); + + return browseButton; + } + + + protected JButton createOptimizationsButton(final JTextField textField) + { + final OptimizationsDialog optimizationsDialog = new OptimizationsDialog(ProGuardGUI.this); + + JButton optimizationsButton = new JButton(msg("select")); + optimizationsButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // Update the dialog. + optimizationsDialog.setFilter(textField.getText()); + + int returnValue = optimizationsDialog.showDialog(); + if (returnValue == OptimizationsDialog.APPROVE_OPTION) + { + // Update the text field. + textField.setText(optimizationsDialog.getFilter()); + } + } + }); + + return optimizationsButton; + } + + + /** + * Sets the preferred sizes of the given components to the maximum of their + * current preferred sizes. + */ + private void setCommonPreferredSize(List components) + { + // Find the maximum preferred size. + Dimension maximumSize = null; + for (int index = 0; index < components.size(); index++) + { + JComponent component = (JComponent)components.get(index); + Dimension size = component.getPreferredSize(); + if (maximumSize == null || + size.getWidth() > maximumSize.getWidth()) + { + maximumSize = size; + } + } + + // Set the size that we found as the preferred size for all components. + for (int index = 0; index < components.size(); index++) + { + JComponent component = (JComponent)components.get(index); + component.setPreferredSize(maximumSize); + } + } + + + /** + * Updates to GUI settings to reflect the given ProGuard configuration. + */ + private void setProGuardConfiguration(Configuration configuration) + { + // Set up the input and output jars and directories. + programPanel.setClassPath(configuration.programJars); + libraryPanel.setClassPath(configuration.libraryJars); + + // Set up the boilerplate keep options. + for (int index = 0; index < boilerplateKeep.length; index++) + { + String classNames = + findMatchingKeepSpecifications(boilerplateKeep[index], + configuration.keep); + + boilerplateKeepCheckBoxes[index].setSelected(classNames != null); + boilerplateKeepTextFields[index].setText(classNames == null ? "*" : classNames); + } + + + // Set up the boilerplate keep names options. + for (int index = 0; index < boilerplateKeepNames.length; index++) + { + String classNames = + findMatchingKeepSpecifications(boilerplateKeepNames[index], + configuration.keep); + + boilerplateKeepNamesCheckBoxes[index].setSelected(classNames != null); + boilerplateKeepNamesTextFields[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(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. + for (int index = 0; index < boilerplateNoSideEffectMethods.length; index++) + { + boolean found = + findClassSpecification(boilerplateNoSideEffectMethods[index], + configuration.assumeNoSideEffects); + + boilerplateNoSideEffectMethodCheckBoxes[index].setSelected(found); + } + + // Set up the additional keep options. Note that the matched boilerplate + // options have been removed from the list. + additionalNoSideEffectsPanel.setClassSpecifications(configuration.assumeNoSideEffects); + + // Set up the "why are you keeping" options. + whyAreYouKeepingPanel.setClassSpecifications(configuration.whyAreYouKeeping); + + // Set up the other options. + shrinkCheckBox .setSelected(configuration.shrink); + printUsageCheckBox .setSelected(configuration.printUsage != null); + + optimizeCheckBox .setSelected(configuration.optimize); + allowAccessModificationCheckBox .setSelected(configuration.allowAccessModification); + mergeInterfacesAggressivelyCheckBox .setSelected(configuration.mergeInterfacesAggressively); + optimizationPassesSpinner.getModel() .setValue(new Integer(configuration.optimizationPasses)); + + obfuscateCheckBox .setSelected(configuration.obfuscate); + printMappingCheckBox .setSelected(configuration.printMapping != null); + applyMappingCheckBox .setSelected(configuration.applyMapping != null); + obfuscationDictionaryCheckBox .setSelected(configuration.obfuscationDictionary != null); + classObfuscationDictionaryCheckBox .setSelected(configuration.classObfuscationDictionary != null); + packageObfuscationDictionaryCheckBox .setSelected(configuration.packageObfuscationDictionary != null); + overloadAggressivelyCheckBox .setSelected(configuration.overloadAggressively); + useUniqueClassMemberNamesCheckBox .setSelected(configuration.useUniqueClassMemberNames); + useMixedCaseClassNamesCheckBox .setSelected(configuration.useMixedCaseClassNames); + keepPackageNamesCheckBox .setSelected(configuration.keepPackageNames != null); + flattenPackageHierarchyCheckBox .setSelected(configuration.flattenPackageHierarchy != null); + repackageClassesCheckBox .setSelected(configuration.repackageClasses != null); + keepAttributesCheckBox .setSelected(configuration.keepAttributes != null); + keepParameterNamesCheckBox .setSelected(configuration.keepParameterNames); + newSourceFileAttributeCheckBox .setSelected(configuration.newSourceFileAttribute != null); + adaptClassStringsCheckBox .setSelected(configuration.adaptClassStrings != null); + adaptResourceFileNamesCheckBox .setSelected(configuration.adaptResourceFileNames != null); + adaptResourceFileContentsCheckBox .setSelected(configuration.adaptResourceFileContents != null); + + preverifyCheckBox .setSelected(configuration.preverify); + microEditionCheckBox .setSelected(configuration.microEdition); + targetCheckBox .setSelected(configuration.targetClassVersion != 0); + + verboseCheckBox .setSelected(configuration.verbose); + noteCheckBox .setSelected(configuration.note == null || !configuration.note.isEmpty()); + warnCheckBox .setSelected(configuration.warn == null || !configuration.warn.isEmpty()); + ignoreWarningsCheckBox .setSelected(configuration.ignoreWarnings); + skipNonPublicLibraryClassesCheckBox .setSelected(configuration.skipNonPublicLibraryClasses); + skipNonPublicLibraryClassMembersCheckBox.setSelected(configuration.skipNonPublicLibraryClassMembers); + keepDirectoriesCheckBox .setSelected(configuration.keepDirectories != null); + forceProcessingCheckBox .setSelected(configuration.lastModified == Long.MAX_VALUE); + printSeedsCheckBox .setSelected(configuration.printSeeds != null); + printConfigurationCheckBox .setSelected(configuration.printConfiguration != null); + dumpCheckBox .setSelected(configuration.dump != null); + + printUsageTextField .setText(fileName(configuration.printUsage)); + optimizationsTextField .setText(configuration.optimizations == null ? OPTIMIZATIONS_DEFAULT : ListUtil.commaSeparatedString(configuration.optimizations, true)); + printMappingTextField .setText(fileName(configuration.printMapping)); + applyMappingTextField .setText(fileName(configuration.applyMapping)); + obfuscationDictionaryTextField .setText(fileName(configuration.obfuscationDictionary)); + classObfuscationDictionaryTextField .setText(fileName(configuration.classObfuscationDictionary)); + packageObfuscationDictionaryTextField .setText(fileName(configuration.packageObfuscationDictionary)); + keepPackageNamesTextField .setText(configuration.keepPackageNames == null ? "" : ClassUtil.externalClassName(ListUtil.commaSeparatedString(configuration.keepPackageNames, true))); + flattenPackageHierarchyTextField .setText(configuration.flattenPackageHierarchy); + repackageClassesTextField .setText(configuration.repackageClasses); + keepAttributesTextField .setText(configuration.keepAttributes == null ? KEEP_ATTRIBUTE_DEFAULT : ListUtil.commaSeparatedString(configuration.keepAttributes, true)); + newSourceFileAttributeTextField .setText(configuration.newSourceFileAttribute == null ? SOURCE_FILE_ATTRIBUTE_DEFAULT : configuration.newSourceFileAttribute); + adaptClassStringsTextField .setText(configuration.adaptClassStrings == null ? "" : ClassUtil.externalClassName(ListUtil.commaSeparatedString(configuration.adaptClassStrings, true))); + adaptResourceFileNamesTextField .setText(configuration.adaptResourceFileNames == null ? ADAPT_RESOURCE_FILE_NAMES_DEFAULT : ListUtil.commaSeparatedString(configuration.adaptResourceFileNames, true)); + adaptResourceFileContentsTextField .setText(configuration.adaptResourceFileContents == null ? ADAPT_RESOURCE_FILE_CONTENTS_DEFAULT : ListUtil.commaSeparatedString(configuration.adaptResourceFileContents, true)); + noteTextField .setText(ListUtil.commaSeparatedString(configuration.note, true)); + warnTextField .setText(ListUtil.commaSeparatedString(configuration.warn, true)); + keepDirectoriesTextField .setText(ListUtil.commaSeparatedString(configuration.keepDirectories, true)); + 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) + { + reTraceMappingTextField.setText(fileName(configuration.printMapping)); + } + } + + + /** + * Returns the ProGuard configuration that reflects the current GUI settings. + */ + private Configuration getProGuardConfiguration() + { + Configuration configuration = new Configuration(); + + // Get the input and output jars and directories. + configuration.programJars = programPanel.getClassPath(); + configuration.libraryJars = libraryPanel.getClassPath(); + + List keep = new ArrayList(); + + // Collect the additional keep options. + List additionalKeep = additionalKeepPanel.getClassSpecifications(); + if (additionalKeep != null) + { + keep.addAll(additionalKeep); + } + + // Collect the additional keep names options. + List additionalKeepNames = additionalKeepNamesPanel.getClassSpecifications(); + if (additionalKeepNames != null) + { + 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. + for (int index = 0; index < boilerplateKeepNames.length; index++) + { + if (boilerplateKeepNamesCheckBoxes[index].isSelected()) + { + keep.add(classSpecification(boilerplateKeepNames[index], + boilerplateKeepNamesTextFields[index].getText())); + } + } + + // Put the list of keep specifications in the configuration. + if (keep.size() > 0) + { + configuration.keep = keep; + } + + + // Collect the boilerplate "no side effect methods" options. + List noSideEffectMethods = new ArrayList(); + + for (int index = 0; index < boilerplateNoSideEffectMethods.length; index++) + { + if (boilerplateNoSideEffectMethodCheckBoxes[index].isSelected()) + { + noSideEffectMethods.add(boilerplateNoSideEffectMethods[index]); + } + } + + // Collect the additional "no side effect methods" options. + List additionalNoSideEffectOptions = additionalNoSideEffectsPanel.getClassSpecifications(); + if (additionalNoSideEffectOptions != null) + { + noSideEffectMethods.addAll(additionalNoSideEffectOptions); + } + + // Put the list of "no side effect methods" options in the configuration. + if (noSideEffectMethods.size() > 0) + { + configuration.assumeNoSideEffects = noSideEffectMethods; + } + + + // Collect the "why are you keeping" options. + configuration.whyAreYouKeeping = whyAreYouKeepingPanel.getClassSpecifications(); + + + // Get the other options. + configuration.shrink = shrinkCheckBox .isSelected(); + configuration.printUsage = printUsageCheckBox .isSelected() ? new File(printUsageTextField .getText()) : null; + + configuration.optimize = optimizeCheckBox .isSelected(); + configuration.allowAccessModification = allowAccessModificationCheckBox .isSelected(); + configuration.mergeInterfacesAggressively = mergeInterfacesAggressivelyCheckBox .isSelected(); + configuration.optimizations = optimizationsTextField.getText().length() > 1 ? ListUtil.commaSeparatedList(optimizationsTextField .getText()) : null; + configuration.optimizationPasses = ((SpinnerNumberModel)optimizationPassesSpinner.getModel()).getNumber().intValue(); + + configuration.obfuscate = obfuscateCheckBox .isSelected(); + configuration.printMapping = printMappingCheckBox .isSelected() ? new File(printMappingTextField .getText()) : null; + configuration.applyMapping = applyMappingCheckBox .isSelected() ? new File(applyMappingTextField .getText()) : null; + configuration.obfuscationDictionary = obfuscationDictionaryCheckBox .isSelected() ? new File(obfuscationDictionaryTextField .getText()) : null; + configuration.classObfuscationDictionary = classObfuscationDictionaryCheckBox .isSelected() ? new File(classObfuscationDictionaryTextField .getText()) : null; + configuration.packageObfuscationDictionary = packageObfuscationDictionaryCheckBox .isSelected() ? new File(packageObfuscationDictionaryTextField .getText()) : null; + configuration.overloadAggressively = overloadAggressivelyCheckBox .isSelected(); + configuration.useUniqueClassMemberNames = useUniqueClassMemberNamesCheckBox .isSelected(); + configuration.useMixedCaseClassNames = useMixedCaseClassNamesCheckBox .isSelected(); + configuration.keepPackageNames = keepPackageNamesCheckBox .isSelected() ? keepPackageNamesTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(keepPackageNamesTextField.getText())) : new ArrayList() : null; + configuration.flattenPackageHierarchy = flattenPackageHierarchyCheckBox .isSelected() ? ClassUtil.internalClassName(flattenPackageHierarchyTextField .getText()) : null; + configuration.repackageClasses = repackageClassesCheckBox .isSelected() ? ClassUtil.internalClassName(repackageClassesTextField .getText()) : null; + configuration.keepAttributes = keepAttributesCheckBox .isSelected() ? ListUtil.commaSeparatedList(keepAttributesTextField .getText()) : null; + configuration.keepParameterNames = keepParameterNamesCheckBox .isSelected(); + configuration.newSourceFileAttribute = newSourceFileAttributeCheckBox .isSelected() ? newSourceFileAttributeTextField .getText() : null; + configuration.adaptClassStrings = adaptClassStringsCheckBox .isSelected() ? adaptClassStringsTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(adaptClassStringsTextField.getText())) : new ArrayList() : null; + configuration.adaptResourceFileNames = adaptResourceFileNamesCheckBox .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileNamesTextField .getText()) : null; + configuration.adaptResourceFileContents = adaptResourceFileContentsCheckBox .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileContentsTextField .getText()) : null; + + configuration.preverify = preverifyCheckBox .isSelected(); + configuration.microEdition = microEditionCheckBox .isSelected(); + configuration.targetClassVersion = targetCheckBox .isSelected() ? ClassUtil.internalClassVersion(targetComboBox.getSelectedItem().toString()) : 0; + + configuration.verbose = verboseCheckBox .isSelected(); + configuration.note = noteCheckBox .isSelected() ? noteTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(noteTextField.getText())) : null : new ArrayList(); + configuration.warn = warnCheckBox .isSelected() ? warnTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(warnTextField.getText())) : null : new ArrayList(); + configuration.ignoreWarnings = ignoreWarningsCheckBox .isSelected(); + configuration.skipNonPublicLibraryClasses = skipNonPublicLibraryClassesCheckBox .isSelected(); + configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembersCheckBox.isSelected(); + configuration.keepDirectories = keepDirectoriesCheckBox .isSelected() ? keepDirectoriesTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(keepDirectoriesTextField.getText())) : new ArrayList() : null; + configuration.lastModified = forceProcessingCheckBox .isSelected() ? Long.MAX_VALUE : System.currentTimeMillis(); + configuration.printSeeds = printSeedsCheckBox .isSelected() ? new File(printSeedsTextField .getText()) : null; + configuration.printConfiguration = printConfigurationCheckBox .isSelected() ? new File(printConfigurationTextField .getText()) : null; + configuration.dump = dumpCheckBox .isSelected() ? new File(dumpTextField .getText()) : null; + + return configuration; + } + + + /** + * Looks in the given list for a class specification that is identical to + * the given template. Returns true if it is found, and removes the matching + * class specification as a side effect. + */ + private boolean findClassSpecification(ClassSpecification classSpecificationTemplate, + List classSpecifications) + { + if (classSpecifications == null) + { + return false; + } + + for (int index = 0; index < classSpecifications.size(); index++) + { + if (classSpecificationTemplate.equals(classSpecifications.get(index))) + { + // Remove the matching option as a side effect. + classSpecifications.remove(index); + + return true; + } + } + + return false; + } + + + /** + * 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++) + { + KeepClassSpecification keepClassSpecification = + (KeepClassSpecification)keepSpecifications.get(index); + + if (keepClassSpecification.allowShrinking == allowShrinking) + { + filteredKeepSpecifications.add(keepClassSpecification); + } + } + + 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 findMatchingKeepSpecifications(KeepClassSpecification keepClassSpecificationTemplate, + List keepSpecifications) + { + if (keepSpecifications == null) + { + return null; + } + + StringBuffer buffer = null; + + for (int index = 0; index < keepSpecifications.size(); index++) + { + KeepClassSpecification listedKeepClassSpecification = + (KeepClassSpecification)keepSpecifications.get(index); + String className = listedKeepClassSpecification.className; + keepClassSpecificationTemplate.className = className; + if (keepClassSpecificationTemplate.equals(listedKeepClassSpecification)) + { + if (buffer == null) + { + buffer = new StringBuffer(); + } + else + { + buffer.append(','); + } + buffer.append(className == null ? "*" : ClassUtil.externalClassName(className)); + + // Remove the matching option as a side effect. + keepSpecifications.remove(index--); + } + } + + return buffer == null ? null : buffer.toString(); + } + + + /** + * 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) + { + // Create a copy of the template. + ClassSpecification classSpecification = + (ClassSpecification)classSpecificationTemplate.clone(); + + // Set the class name in the copy. + classSpecification.className = + className.equals("") || + className.equals("*") ? + null : + ClassUtil.internalClassName(className); + + // Return the modified copy. + return classSpecification; + } + + + // Methods and internal classes related to actions. + + /** + * Loads the given ProGuard configuration into the GUI. + */ + private void loadConfiguration(File file) + { + // Set the default directory and file in the file choosers. + configurationChooser.setSelectedFile(file.getAbsoluteFile()); + fileChooser.setCurrentDirectory(file.getAbsoluteFile().getParentFile()); + + try + { + // Parse the configuration file. + ConfigurationParser parser = new ConfigurationParser(file, + System.getProperties()); + + Configuration configuration = new Configuration(); + + try + { + parser.parse(configuration); + + // Let the GUI reflect the configuration. + setProGuardConfiguration(configuration); + } + catch (ParseException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantParseConfigurationFile", file.getPath()), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + finally + { + parser.close(); + } + } + catch (IOException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantOpenConfigurationFile", file.getPath()), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + } + + + /** + * Loads the given ProGuard configuration into the GUI. + */ + private void loadConfiguration(URL url) + { + try + { + // Parse the configuration file. + ConfigurationParser parser = new ConfigurationParser(url, + System.getProperties()); + + Configuration configuration = new Configuration(); + + try + { + parser.parse(configuration); + + // Let the GUI reflect the configuration. + setProGuardConfiguration(configuration); + } + catch (ParseException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantParseConfigurationFile", url), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + finally + { + parser.close(); + } + } + catch (IOException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantOpenConfigurationFile", url), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + } + + + /** + * Saves the current ProGuard configuration to the given file. + */ + private void saveConfiguration(File file) + { + try + { + // Save the configuration file. + ConfigurationWriter writer = new ConfigurationWriter(file); + writer.write(getProGuardConfiguration()); + writer.close(); + } + catch (Exception ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantSaveConfigurationFile", file.getPath()), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + } + + + /** + * Loads the given stack trace into the GUI. + */ + private void loadStackTrace(File file) + { + try + { + StringBuffer buffer = new StringBuffer(1024); + + Reader reader = new BufferedReader(new FileReader(file)); + 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(buffer.toString()); + } + catch (IOException ex) + { + JOptionPane.showMessageDialog(getContentPane(), + msg("cantOpenStackTraceFile", fileName(file)), + msg("warning"), + JOptionPane.ERROR_MESSAGE); + } + } + + + /** + * This ActionListener loads a ProGuard configuration file and initializes + * the GUI accordingly. + */ + private class MyLoadConfigurationActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + configurationChooser.setDialogTitle(msg("selectConfigurationFile")); + + int returnValue = configurationChooser.showOpenDialog(ProGuardGUI.this); + if (returnValue == JFileChooser.APPROVE_OPTION) + { + loadConfiguration(configurationChooser.getSelectedFile()); + } + } + } + + + /** + * This ActionListener saves a ProGuard configuration file based on the + * current GUI settings. + */ + private class MySaveConfigurationActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + configurationChooser.setDialogTitle(msg("saveConfigurationFile")); + + int returnVal = configurationChooser.showSaveDialog(ProGuardGUI.this); + if (returnVal == JFileChooser.APPROVE_OPTION) + { + saveConfiguration(configurationChooser.getSelectedFile()); + } + } + } + + + /** + * This ActionListener displays the ProGuard configuration specified by the + * current GUI settings. + */ + private class MyViewConfigurationActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + // Make sure System.out has not been redirected yet. + if (!systemOutRedirected) + { + consoleTextArea.setText(""); + + TextAreaOutputStream outputStream = + new TextAreaOutputStream(consoleTextArea); + + try + { + // TODO: write out relative path names and path names with system properties. + + // Write the configuration. + ConfigurationWriter writer = new ConfigurationWriter(outputStream); + try + { + writer.write(getProGuardConfiguration()); + } + finally + { + writer.close(); + } + } + catch (IOException ex) + { + // This shouldn't happen. + } + + // Scroll to the top of the configuration. + consoleTextArea.setCaretPosition(0); + } + } + } + + + /** + * This ActionListener executes ProGuard based on the current GUI settings. + */ + private class MyProcessActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + // Make sure System.out has not been redirected yet. + if (!systemOutRedirected) + { + systemOutRedirected = true; + + // Get the informational configuration file name. + File configurationFile = configurationChooser.getSelectedFile(); + String configurationFileName = configurationFile != null ? + configurationFile.getName() : + msg("sampleConfigurationFileName"); + + // Create the ProGuard thread. + Thread proGuardThread = + new Thread(new ProGuardRunnable(consoleTextArea, + getProGuardConfiguration(), + configurationFileName)); + + // Run it. + proGuardThread.start(); + } + } + } + + + /** + * This ActionListener loads an obfuscated stack trace from a file and puts + * it in the proper text area. + */ + private class MyLoadStackTraceActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + fileChooser.setDialogTitle(msg("selectStackTraceFile")); + fileChooser.setSelectedFile(null); + + int returnValue = fileChooser.showOpenDialog(ProGuardGUI.this); + if (returnValue == JFileChooser.APPROVE_OPTION) + { + + loadStackTrace(fileChooser.getSelectedFile()); + } + } + } + + + /** + * This ActionListener executes ReTrace based on the current GUI settings. + */ + private class MyReTraceActionListener implements ActionListener + { + public void actionPerformed(ActionEvent e) + { + // Make sure System.out has not been redirected yet. + if (!systemOutRedirected) + { + systemOutRedirected = true; + + boolean verbose = reTraceVerboseCheckBox.isSelected(); + File retraceMappingFile = new File(reTraceMappingTextField.getText()); + String stackTrace = stackTraceTextArea.getText(); + + // Create the ReTrace runnable. + Runnable reTraceRunnable = new ReTraceRunnable(reTraceTextArea, + verbose, + retraceMappingFile, + stackTrace); + + // Run it in this thread, because it won't take long anyway. + reTraceRunnable.run(); + } + } + } + + + // Small utility methods. + + /** + * Returns the canonical file name for the given file, or the empty string + * if the file name is empty. + */ + private String fileName(File file) + { + if (file == null) + { + return ""; + } + else + { + try + { + return file.getCanonicalPath(); + } + catch (IOException ex) + { + return file.getPath(); + } + } + } + + + /** + * 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); + } + + + /** + * Returns the message from the GUI resources that corresponds to the given + * key and argument. + */ + private String msg(String messageKey, + Object messageArgument) + { + return GUIResources.getMessage(messageKey, new Object[] {messageArgument}); + } + + + /** + * The main method for the ProGuard GUI. + */ + public static void main(final String[] args) + { + try + { + SwingUtil.invokeAndWait(new Runnable() + { + public void run() + { + try + { + ProGuardGUI gui = new ProGuardGUI(); + + 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++; + } + + if (argIndex < args.length) + { + System.out.println(gui.getClass().getName() + ": ignoring extra arguments [" + args[argIndex] + "...]"); + } + } + catch (Exception e) + { + System.out.println("Internal problem starting the ProGuard GUI (" + e.getMessage() + ")"); + } + } + }); + } + catch (Exception e) + { + System.out.println("Internal problem starting the ProGuard GUI (" + e.getMessage() + ")"); + } + } +} diff --git a/src/proguard/gui/ProGuardRunnable.java b/src/proguard/gui/ProGuardRunnable.java new file mode 100644 index 000000000..b341b7bb6 --- /dev/null +++ b/src/proguard/gui/ProGuardRunnable.java @@ -0,0 +1,154 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import java.awt.*; +import java.io.PrintStream; + + +/** + * This Runnable runs ProGuard, sending console output to a text + * area and any exceptions to message dialogs. + * + * @see ProGuard + * @author Eric Lafortune + */ +final class ProGuardRunnable implements Runnable +{ + private final JTextArea consoleTextArea; + private final Configuration configuration; + private final String configurationFileName; + + + /** + * Creates a new ProGuardRunnable object. + * @param consoleTextArea the text area to send the console output to. + * @param configuration the ProGuard configuration. + * @param configurationFileName the optional file name of the configuration, + * for informational purposes. + */ + public ProGuardRunnable(JTextArea consoleTextArea, + Configuration configuration, + String configurationFileName) + { + this.consoleTextArea = consoleTextArea; + this.configuration = configuration; + this.configurationFileName = configurationFileName; + } + + + // Implementation for Runnable. + + public void run() + { + consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + consoleTextArea.setText(""); + + // Redirect the System's out and err streams to the console text area. + PrintStream oldOut = System.out; + PrintStream oldErr = System.err; + + PrintStream printStream = + new PrintStream(new TextAreaOutputStream(consoleTextArea), true); + + System.setOut(printStream); + System.setErr(printStream); + + try + { + // Create a new ProGuard object with the GUI's configuration. + ProGuard proGuard = new ProGuard(configuration); + + // Run it. + proGuard.execute(); + + // Print out the completion message. + System.out.println("Processing completed successfully"); + } + catch (Exception ex) + { + //ex.printStackTrace(); + + // Print out the exception message. + System.out.println(ex.getMessage()); + + // Show a dialog as well. + MessageDialogRunnable.showMessageDialog(consoleTextArea, + ex.getMessage(), + msg("errorProcessing"), + JOptionPane.ERROR_MESSAGE); + } + catch (OutOfMemoryError er) + { + // Forget about the ProGuard object as quickly as possible. + System.gc(); + + // Print out a message suggesting what to do next. + System.out.println(msg("outOfMemoryInfo", configurationFileName)); + + // Show a dialog as well. + MessageDialogRunnable.showMessageDialog(consoleTextArea, + msg("outOfMemory"), + msg("errorProcessing"), + JOptionPane.ERROR_MESSAGE); + } + finally + { + // Make sure all output has been sent to the console text area. + printStream.close(); + + // Restore the old System's out and err streams. + System.setOut(oldOut); + System.setErr(oldErr); + } + + consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + + // Reset the global static redirection lock. + ProGuardGUI.systemOutRedirected = false; + } + + + // Small utility methods. + + /** + * Returns the message from the GUI resources that corresponds to the given + * key. + */ + private String msg(String messageKey) + { + return GUIResources.getMessage(messageKey); + } + + + /** + * Returns the message from the GUI resources that corresponds to the given + * key and argument. + */ + private String msg(String messageKey, + Object messageArgument) + { + return GUIResources.getMessage(messageKey, new Object[] {messageArgument}); + } +} diff --git a/src/proguard/gui/ReTraceRunnable.java b/src/proguard/gui/ReTraceRunnable.java new file mode 100644 index 000000000..6f1b13572 --- /dev/null +++ b/src/proguard/gui/ReTraceRunnable.java @@ -0,0 +1,149 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.retrace.ReTrace; + +import javax.swing.*; +import java.awt.*; +import java.io.*; + + +/** + * This Runnable runs ReTrace, sending console output to a text + * area and any exceptions to message dialogs. + * + * @see ReTrace + * @author Eric Lafortune + */ +final class ReTraceRunnable implements Runnable +{ + private final JTextArea consoleTextArea; + private final boolean verbose; + private final File mappingFile; + private final String stackTrace; + + + /** + * Creates a new ProGuardRunnable object. + * @param consoleTextArea the text area to send the console output to. + * @param verbose specifies whether the de-obfuscated stack trace + * should be verbose. + * @param mappingFile the mapping file that was written out by ProGuard. + */ + public ReTraceRunnable(JTextArea consoleTextArea, + boolean verbose, + File mappingFile, + String stackTrace) + { + this.consoleTextArea = consoleTextArea; + this.verbose = verbose; + this.mappingFile = mappingFile; + this.stackTrace = stackTrace; + } + + + // Implementation for Runnable. + + public void run() + { + consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + consoleTextArea.setText(""); + + // Redirect the stack trace string to the System's in stream, and the + // out and err streams to the console text area. + InputStream oldIn = System.in; + PrintStream oldOut = System.out; + PrintStream oldErr = System.err; + + ByteArrayInputStream inputStream = + new ByteArrayInputStream(stackTrace.getBytes()); + + PrintStream printStream = + new PrintStream(new TextAreaOutputStream(consoleTextArea), true); + + System.setIn(inputStream); + System.setOut(printStream); + System.setErr(printStream); + + try + { + // Create a new ProGuard object with the GUI's configuration. + ReTrace reTrace = new ReTrace(ReTrace.STACK_TRACE_EXPRESSION, + verbose, + mappingFile); + + // Run it. + reTrace.execute(); + } + catch (Exception ex) + { + // Print out the exception message. + System.out.println(ex.getMessage()); + + // Show a dialog as well. + MessageDialogRunnable.showMessageDialog(consoleTextArea, + ex.getMessage(), + msg("errorReTracing"), + JOptionPane.ERROR_MESSAGE); + } + catch (OutOfMemoryError er) + { + // Forget about the ProGuard object as quickly as possible. + System.gc(); + + // Print out a message suggesting what to do next. + System.out.println(msg("outOfMemory")); + + // Show a dialog as well. + MessageDialogRunnable.showMessageDialog(consoleTextArea, + msg("outOfMemory"), + msg("errorReTracing"), + JOptionPane.ERROR_MESSAGE); + } + + // Make sure all output has been sent to the console text area. + printStream.flush(); + + // Restore the old System's in, out, and err streams. + System.setIn(oldIn); + System.setOut(oldOut); + System.setErr(oldErr); + + consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + consoleTextArea.setCaretPosition(0); + + // Reset the global static redirection lock. + ProGuardGUI.systemOutRedirected = false; + } + + + // Small utility methods. + + /** + * Returns the message from the GUI resources that corresponds to the given + * key. + */ + private String msg(String messageKey) + { + return GUIResources.getMessage(messageKey); + } +} diff --git a/src/proguard/gui/SwingUtil.java b/src/proguard/gui/SwingUtil.java new file mode 100644 index 000000000..373fdc9d4 --- /dev/null +++ b/src/proguard/gui/SwingUtil.java @@ -0,0 +1,82 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.gui; + +import javax.swing.*; +import java.lang.reflect.InvocationTargetException; + + +/** + * This utility class provides variants of the invocation method from the + * SwingUtilities class. + * + * @see SwingUtilities + * @author Eric Lafortune + */ +public 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(Runnable) + * @param runnable the Runnable to be executed. + */ + public static void invokeAndWait(Runnable runnable) + throws InterruptedException, InvocationTargetException + { + try + { + if (SwingUtilities.isEventDispatchThread()) + { + runnable.run(); + } + else + { + SwingUtilities.invokeAndWait(runnable); + } + } + catch (Exception ex) + { + // Ignore any exceptions. + } + } + + + /** + * 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(Runnable) + * @param runnable the Runnable to be executed. + */ + public static void invokeLater(Runnable runnable) + { + if (SwingUtilities.isEventDispatchThread()) + { + runnable.run(); + } + else + { + SwingUtilities.invokeLater(runnable); + } + } +} diff --git a/src/proguard/gui/TabbedPane.java b/src/proguard/gui/TabbedPane.java new file mode 100644 index 000000000..c6b6678bd --- /dev/null +++ b/src/proguard/gui/TabbedPane.java @@ -0,0 +1,229 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.gui; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; + + +/** + * This Jpanel is similar to a JTabbedPane. + * It uses buttons on the left-hand side to switch between panels. + * An image can be added below these buttons. + * Some methods are provided to switch between tabs. + * + * @author Eric Lafortune + */ +public class TabbedPane + extends JPanel +{ + private final CardLayout cardLayout = new CardLayout(); + private final JPanel cardPanel = new JPanel(cardLayout); + private final ButtonGroup buttonGroup = new ButtonGroup(); + + + /** + * Creates a new TabbedPane. + */ + public TabbedPane() + { + GridBagLayout layout = new GridBagLayout(); + setLayout(layout); + + GridBagConstraints cardConstraints = new GridBagConstraints(); + cardConstraints.gridx = 1; + cardConstraints.gridy = 0; + cardConstraints.gridheight = GridBagConstraints.REMAINDER; + cardConstraints.fill = GridBagConstraints.BOTH; + cardConstraints.weightx = 1.0; + cardConstraints.weighty = 1.0; + cardConstraints.anchor = GridBagConstraints.NORTHWEST; + + add(cardPanel, cardConstraints); + } + + + /** + * Adds a component with a given title to the tabbed pane. + * + * @param title the title that will be used in the tab button. + * @param component the component that will be added as a tab. + */ + public Component add(final String title, Component component) + { + GridBagConstraints buttonConstraints = new GridBagConstraints(); + buttonConstraints.gridx = 0; + buttonConstraints.fill = GridBagConstraints.HORIZONTAL; + buttonConstraints.anchor = GridBagConstraints.NORTHWEST; + buttonConstraints.ipadx = 10; + buttonConstraints.ipady = 4; + + JToggleButton button = new JToggleButton(title); + + // Let the button react on the mouse press, instead of waiting for the + // mouse release. + button.setModel(new JToggleButton.ToggleButtonModel() + { + public void setPressed(boolean b) + { + if ((isPressed() == b) || !isEnabled()) + { + return; + } + + if (!b && isArmed()) + { + setSelected(!this.isSelected()); + } + + if (b) + { + stateMask |= PRESSED; + } + else + { + stateMask &= ~PRESSED; + } + + fireStateChanged(); + + if (isPressed()) + { + fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getActionCommand())); + } + } + + }); + + // Switch to the tab on a button press. + button.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + cardLayout.show(cardPanel, title); + } + }); + + // Only one button can be selected at the same time. + buttonGroup.add(button); + + // If this is the first tab, make sure its button is selected. + if (cardPanel.getComponentCount() == 0) + { + button.setSelected(true); + } + + // Add the button and its panel. + add(button, buttonConstraints); + cardPanel.add(title, component); + + return component; + } + + + /** + * Adds an image below the tab buttons, after all tabs have been added. + * The image will only be as visible as permitted by the available space. + * + * @param image the image. + * @return the component containing the image. + */ + public Component addImage(final Image image) + { + GridBagConstraints imageConstraints = new GridBagConstraints(); + imageConstraints.gridx = 0; + imageConstraints.weighty = 1.0; + imageConstraints.fill = GridBagConstraints.BOTH; + imageConstraints.anchor = GridBagConstraints.SOUTHWEST; + + JButton component = new JButton(new ImageIcon(image)); + component.setFocusPainted(false); + component.setFocusable(false); + component.setRequestFocusEnabled(false); + component.setRolloverEnabled(false); + component.setMargin(new Insets(0, 0, 0, 0)); + component.setHorizontalAlignment(JButton.LEFT); + component.setVerticalAlignment(JButton.BOTTOM); + component.setPreferredSize(new Dimension(0, 0)); + + add(component, imageConstraints); + + return component; + } + + + /** + * Selects the first tab. + */ + public void first() + { + cardLayout.first(cardPanel); + updateButtonSelection(); + } + + + /** + * Selects the last tab. + */ + public void last() + { + cardLayout.last(cardPanel); + updateButtonSelection(); + } + + + /** + * Selects the previous tab. + */ + public void previous() + { + cardLayout.previous(cardPanel); + updateButtonSelection(); + } + + + /** + * Selects the next tab. + */ + public void next() + { + cardLayout.next(cardPanel); + updateButtonSelection(); + } + + + /** + * Lets the button selection reflect the currently visible panel. + */ + private void updateButtonSelection() + { + int count = cardPanel.getComponentCount(); + for (int index = 0 ; index < count ; index++) { + Component card = cardPanel.getComponent(index); + if (card.isShowing()) + { + JToggleButton button = (JToggleButton)getComponent(index+1); + button.setSelected(true); + } + } + } +} diff --git a/src/proguard/gui/TextAreaOutputStream.java b/src/proguard/gui/TextAreaOutputStream.java new file mode 100644 index 000000000..84ba562a3 --- /dev/null +++ b/src/proguard/gui/TextAreaOutputStream.java @@ -0,0 +1,81 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.gui; + +import javax.swing.*; +import java.io.*; +import java.lang.reflect.InvocationTargetException; + + +/** + * This PrintStream appends its output to a given text area. + * + * @author Eric Lafortune + */ +final class TextAreaOutputStream extends FilterOutputStream implements Runnable +{ + private final JTextArea textArea; + + + public TextAreaOutputStream(JTextArea textArea) + { + super(new ByteArrayOutputStream()); + + this.textArea = textArea; + } + + + // Implementation for FilterOutputStream. + + public void flush() throws IOException + { + super.flush(); + + try + { + // Append the accumulated buffer contents to the text area. + SwingUtil.invokeAndWait(this); + } + catch (Exception e) + { + // Nothing. + } + } + + + // Implementation for Runnable. + + public void run() + { + ByteArrayOutputStream out = (ByteArrayOutputStream)super.out; + + // Has any new text been written? + String text = out.toString(); + if (text.length() > 0) + { + // Append the accumulated text to the text area. + textArea.append(text); + + // Clear the buffer. + out.reset(); + } + } +} diff --git a/src/proguard/gui/arrow.gif b/src/proguard/gui/arrow.gif new file mode 100644 index 0000000000000000000000000000000000000000..c58e34ea4726f80091a00338154b01a88132c1b5 GIT binary patch literal 112 zcmZ?wbhEHblwlBHSjfQe|NsB9XU{%+_6*2S{K>+|#lXycl$N{s9Pkgq@*C0*g37Za; + public protected ; +} + +# Also keep - Enumerations. Keep the special static methods that are required in +# enumeration classes. +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# 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 implementations of java.beans.BeanInfo. +-keep class * implements java.beans.BeanInfo + +# Also keep - Bean classes. Keep all specified classes, along with their getters +# and setters. +-keep class * { + void set*(***); + void set*(int,***); + + boolean is*(); + boolean is*(int); + + *** get*(); + *** get*(int); +} + +# 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 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 interfaces that extend the +# java.rmi.Remote interface, and their methods. +-keep interface * extends java.rmi.Remote { + ; +} + +# 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 { + (java.rmi.activation.ActivationID,java.rmi.MarshalledObject); +} + +# Keep names - Native method names. Keep all native class/method names. +-keepclasseswithmembernames class * { + native ; +} + +# 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); +} + +# Remove - System method calls. Remove all invocations of System +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.System { + public static long currentTimeMillis(); + static java.lang.Class getCallerClass(); + public static int identityHashCode(java.lang.Object); + public static java.lang.SecurityManager getSecurityManager(); + public static java.util.Properties getProperties(); + public static java.lang.String getProperty(java.lang.String); + public static java.lang.String getenv(java.lang.String); + public static java.lang.String mapLibraryName(java.lang.String); + public static java.lang.String getProperty(java.lang.String,java.lang.String); +} + +# Remove - Math method calls. Remove all invocations of Math +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.Math { + public static double sin(double); + public static double cos(double); + public static double tan(double); + public static double asin(double); + public static double acos(double); + public static double atan(double); + public static double toRadians(double); + public static double toDegrees(double); + public static double exp(double); + public static double log(double); + public static double log10(double); + public static double sqrt(double); + public static double cbrt(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 int round(float); + public static long round(double); + public static double random(); + public static int abs(int); + 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 double ulp(double); + public static float ulp(float); + public static double signum(double); + public static float signum(float); + public static double sinh(double); + public static double cosh(double); + public static double tanh(double); + public static double hypot(double,double); + public static double expm1(double); + public static double log1p(double); +} + +# Remove - Number method calls. Remove all invocations of Number +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.* extends java.lang.Number { + public static java.lang.String toString(byte); + public static java.lang.Byte valueOf(byte); + public static byte parseByte(java.lang.String); + public static byte parseByte(java.lang.String,int); + public static java.lang.Byte valueOf(java.lang.String,int); + public static java.lang.Byte valueOf(java.lang.String); + public static java.lang.Byte decode(java.lang.String); + public int compareTo(java.lang.Byte); + + public static java.lang.String toString(short); + public static short parseShort(java.lang.String); + public static short parseShort(java.lang.String,int); + public static java.lang.Short valueOf(java.lang.String,int); + public static java.lang.Short valueOf(java.lang.String); + public static java.lang.Short valueOf(short); + public static java.lang.Short decode(java.lang.String); + public static short reverseBytes(short); + public int compareTo(java.lang.Short); + + public static java.lang.String toString(int,int); + public static java.lang.String toHexString(int); + public static java.lang.String toOctalString(int); + public static java.lang.String toBinaryString(int); + public static java.lang.String toString(int); + public static int parseInt(java.lang.String,int); + public static int parseInt(java.lang.String); + public static java.lang.Integer valueOf(java.lang.String,int); + public static java.lang.Integer valueOf(java.lang.String); + public static java.lang.Integer valueOf(int); + public static java.lang.Integer getInteger(java.lang.String); + public static java.lang.Integer getInteger(java.lang.String,int); + public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer); + public static java.lang.Integer decode(java.lang.String); + public static int highestOneBit(int); + public static int lowestOneBit(int); + public static int numberOfLeadingZeros(int); + public static int numberOfTrailingZeros(int); + public static int bitCount(int); + public static int rotateLeft(int,int); + public static int rotateRight(int,int); + public static int reverse(int); + public static int signum(int); + public static int reverseBytes(int); + public int compareTo(java.lang.Integer); + + public static java.lang.String toString(long,int); + public static java.lang.String toHexString(long); + public static java.lang.String toOctalString(long); + public static java.lang.String toBinaryString(long); + public static java.lang.String toString(long); + public static long parseLong(java.lang.String,int); + public static long parseLong(java.lang.String); + public static java.lang.Long valueOf(java.lang.String,int); + public static java.lang.Long valueOf(java.lang.String); + public static java.lang.Long valueOf(long); + public static java.lang.Long decode(java.lang.String); + public static java.lang.Long getLong(java.lang.String); + public static java.lang.Long getLong(java.lang.String,long); + public static java.lang.Long getLong(java.lang.String,java.lang.Long); + public static long highestOneBit(long); + public static long lowestOneBit(long); + public static int numberOfLeadingZeros(long); + public static int numberOfTrailingZeros(long); + public static int bitCount(long); + public static long rotateLeft(long,int); + public static long rotateRight(long,int); + public static long reverse(long); + public static int signum(long); + public static long reverseBytes(long); + public int compareTo(java.lang.Long); + + public static java.lang.String toString(float); + public static java.lang.String toHexString(float); + public static java.lang.Float valueOf(java.lang.String); + public static java.lang.Float valueOf(float); + public static float parseFloat(java.lang.String); + public static boolean isNaN(float); + public static boolean isInfinite(float); + public static int floatToIntBits(float); + public static int floatToRawIntBits(float); + public static float intBitsToFloat(int); + public static int compare(float,float); + public boolean isNaN(); + public boolean isInfinite(); + public int compareTo(java.lang.Float); + + public static java.lang.String toString(double); + public static java.lang.String toHexString(double); + public static java.lang.Double valueOf(java.lang.String); + public static java.lang.Double valueOf(double); + public static double parseDouble(java.lang.String); + public static boolean isNaN(double); + public static boolean isInfinite(double); + public static long doubleToLongBits(double); + public static long doubleToRawLongBits(double); + public static double longBitsToDouble(long); + public static int compare(double,double); + public boolean isNaN(); + public boolean isInfinite(); + public int compareTo(java.lang.Double); + + public byte byteValue(); + public short shortValue(); + public int intValue(); + public long longValue(); + public float floatValue(); + public double doubleValue(); + + public int compareTo(java.lang.Object); + public boolean equals(java.lang.Object); + public int hashCode(); + public java.lang.String toString(); +} + +# Remove - String method calls. Remove all invocations of String +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.String { + public static java.lang.String copyValueOf(char[]); + public static java.lang.String copyValueOf(char[],int,int); + public static java.lang.String valueOf(boolean); + public static java.lang.String valueOf(char); + public static java.lang.String valueOf(char[]); + public static java.lang.String valueOf(char[],int,int); + public static java.lang.String valueOf(double); + public static java.lang.String valueOf(float); + public static java.lang.String valueOf(int); + public static java.lang.String valueOf(java.lang.Object); + public static java.lang.String valueOf(long); + public boolean contentEquals(java.lang.StringBuffer); + public boolean endsWith(java.lang.String); + public boolean equalsIgnoreCase(java.lang.String); + public boolean equals(java.lang.Object); + public boolean matches(java.lang.String); + public boolean regionMatches(boolean,int,java.lang.String,int,int); + public boolean regionMatches(int,java.lang.String,int,int); + public boolean startsWith(java.lang.String); + public boolean startsWith(java.lang.String,int); + public byte[] getBytes(); + public byte[] getBytes(java.lang.String); + public char charAt(int); + public char[] toCharArray(); + public int compareToIgnoreCase(java.lang.String); + public int compareTo(java.lang.Object); + public int compareTo(java.lang.String); + public int hashCode(); + public int indexOf(int); + public int indexOf(int,int); + public int indexOf(java.lang.String); + public int indexOf(java.lang.String,int); + public int lastIndexOf(int); + public int lastIndexOf(int,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.CharSequence subSequence(int,int); + public java.lang.String concat(java.lang.String); + public java.lang.String replaceAll(java.lang.String,java.lang.String); + public java.lang.String replace(char,char); + public java.lang.String replaceFirst(java.lang.String,java.lang.String); + public java.lang.String[] split(java.lang.String); + public java.lang.String[] split(java.lang.String,int); + public java.lang.String substring(int); + public java.lang.String substring(int,int); + public java.lang.String toLowerCase(); + public java.lang.String toLowerCase(java.util.Locale); + public java.lang.String toString(); + public java.lang.String toUpperCase(); + public java.lang.String toUpperCase(java.util.Locale); + public java.lang.String trim(); +} + +# Remove - StringBuffer method calls. Remove all invocations of StringBuffer +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.StringBuffer { + public java.lang.String toString(); + public char charAt(int); + public int capacity(); + public int codePointAt(int); + public int codePointBefore(int); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + 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. +-assumenosideeffects public class java.lang.StringBuilder { + public java.lang.String toString(); + public char charAt(int); + public int capacity(); + public int codePointAt(int); + public int codePointBefore(int); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + public java.lang.String substring(int,int); +} + +# Remove debugging - Throwable_printStackTrace calls. Remove all invocations of +# Throwable.printStackTrace(). +-assumenosideeffects public class java.lang.Throwable { + public void printStackTrace(); +} + +# Remove debugging - Thread_dumpStack calls. Remove all invocations of +# Thread.dumpStack(). +-assumenosideeffects public class java.lang.Thread { + public static void dumpStack(); +} + +# Remove debugging - All logging API calls. Remove all invocations of the +# logging API whose return values are not used. +-assumenosideeffects public class java.util.logging.* { + ; +} + +# Remove debugging - All Log4j API calls. Remove all invocations of the +# Log4j API whose return values are not used. +-assumenosideeffects public class org.apache.log4j.** { + ; +} diff --git a/src/proguard/gui/default.pro b/src/proguard/gui/default.pro new file mode 100644 index 000000000..752c9b2dd --- /dev/null +++ b/src/proguard/gui/default.pro @@ -0,0 +1,292 @@ +# The default configuration when starting up the GUI. + +-libraryjars /lib/rt.jar + +# Keep - Applications. Keep all application classes, along with their 'main' +# methods. +-keepclasseswithmembers public class * { + public static void main(java.lang.String[]); +} + +# Also keep - Enumerations. Keep the special static methods that are required in +# enumeration classes. +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# 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 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 ; +} + +# Remove - System method calls. Remove all invocations of System +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.System { + public static long currentTimeMillis(); + static java.lang.Class getCallerClass(); + public static int identityHashCode(java.lang.Object); + public static java.lang.SecurityManager getSecurityManager(); + public static java.util.Properties getProperties(); + public static java.lang.String getProperty(java.lang.String); + public static java.lang.String getenv(java.lang.String); + public static java.lang.String mapLibraryName(java.lang.String); + public static java.lang.String getProperty(java.lang.String,java.lang.String); +} + +# Remove - Math method calls. Remove all invocations of Math +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.Math { + public static double sin(double); + public static double cos(double); + public static double tan(double); + public static double asin(double); + public static double acos(double); + public static double atan(double); + public static double toRadians(double); + public static double toDegrees(double); + public static double exp(double); + public static double log(double); + public static double log10(double); + public static double sqrt(double); + public static double cbrt(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 int round(float); + public static long round(double); + public static double random(); + public static int abs(int); + 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 double ulp(double); + public static float ulp(float); + public static double signum(double); + public static float signum(float); + public static double sinh(double); + public static double cosh(double); + public static double tanh(double); + public static double hypot(double,double); + public static double expm1(double); + public static double log1p(double); +} + +# Remove - Number method calls. Remove all invocations of Number +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.* extends java.lang.Number { + public static java.lang.String toString(byte); + public static java.lang.Byte valueOf(byte); + public static byte parseByte(java.lang.String); + public static byte parseByte(java.lang.String,int); + public static java.lang.Byte valueOf(java.lang.String,int); + public static java.lang.Byte valueOf(java.lang.String); + public static java.lang.Byte decode(java.lang.String); + public int compareTo(java.lang.Byte); + public static java.lang.String toString(short); + public static short parseShort(java.lang.String); + public static short parseShort(java.lang.String,int); + public static java.lang.Short valueOf(java.lang.String,int); + public static java.lang.Short valueOf(java.lang.String); + public static java.lang.Short valueOf(short); + public static java.lang.Short decode(java.lang.String); + public static short reverseBytes(short); + public int compareTo(java.lang.Short); + public static java.lang.String toString(int,int); + public static java.lang.String toHexString(int); + public static java.lang.String toOctalString(int); + public static java.lang.String toBinaryString(int); + public static java.lang.String toString(int); + public static int parseInt(java.lang.String,int); + public static int parseInt(java.lang.String); + public static java.lang.Integer valueOf(java.lang.String,int); + public static java.lang.Integer valueOf(java.lang.String); + public static java.lang.Integer valueOf(int); + public static java.lang.Integer getInteger(java.lang.String); + public static java.lang.Integer getInteger(java.lang.String,int); + public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer); + public static java.lang.Integer decode(java.lang.String); + public static int highestOneBit(int); + public static int lowestOneBit(int); + public static int numberOfLeadingZeros(int); + public static int numberOfTrailingZeros(int); + public static int bitCount(int); + public static int rotateLeft(int,int); + public static int rotateRight(int,int); + public static int reverse(int); + public static int signum(int); + public static int reverseBytes(int); + public int compareTo(java.lang.Integer); + public static java.lang.String toString(long,int); + public static java.lang.String toHexString(long); + public static java.lang.String toOctalString(long); + public static java.lang.String toBinaryString(long); + public static java.lang.String toString(long); + public static long parseLong(java.lang.String,int); + public static long parseLong(java.lang.String); + public static java.lang.Long valueOf(java.lang.String,int); + public static java.lang.Long valueOf(java.lang.String); + public static java.lang.Long valueOf(long); + public static java.lang.Long decode(java.lang.String); + public static java.lang.Long getLong(java.lang.String); + public static java.lang.Long getLong(java.lang.String,long); + public static java.lang.Long getLong(java.lang.String,java.lang.Long); + public static long highestOneBit(long); + public static long lowestOneBit(long); + public static int numberOfLeadingZeros(long); + public static int numberOfTrailingZeros(long); + public static int bitCount(long); + public static long rotateLeft(long,int); + public static long rotateRight(long,int); + public static long reverse(long); + public static int signum(long); + public static long reverseBytes(long); + public int compareTo(java.lang.Long); + public static java.lang.String toString(float); + public static java.lang.String toHexString(float); + public static java.lang.Float valueOf(java.lang.String); + public static java.lang.Float valueOf(float); + public static float parseFloat(java.lang.String); + public static boolean isNaN(float); + public static boolean isInfinite(float); + public static int floatToIntBits(float); + public static int floatToRawIntBits(float); + public static float intBitsToFloat(int); + public static int compare(float,float); + public boolean isNaN(); + public boolean isInfinite(); + public int compareTo(java.lang.Float); + public static java.lang.String toString(double); + public static java.lang.String toHexString(double); + public static java.lang.Double valueOf(java.lang.String); + public static java.lang.Double valueOf(double); + public static double parseDouble(java.lang.String); + public static boolean isNaN(double); + public static boolean isInfinite(double); + public static long doubleToLongBits(double); + public static long doubleToRawLongBits(double); + public static double longBitsToDouble(long); + public static int compare(double,double); + public boolean isNaN(); + public boolean isInfinite(); + public int compareTo(java.lang.Double); + public byte byteValue(); + public short shortValue(); + public int intValue(); + public long longValue(); + public float floatValue(); + public double doubleValue(); + public int compareTo(java.lang.Object); + public boolean equals(java.lang.Object); + public int hashCode(); + public java.lang.String toString(); +} + +# Remove - String method calls. Remove all invocations of String +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.String { + public static java.lang.String copyValueOf(char[]); + public static java.lang.String copyValueOf(char[],int,int); + public static java.lang.String valueOf(boolean); + public static java.lang.String valueOf(char); + public static java.lang.String valueOf(char[]); + public static java.lang.String valueOf(char[],int,int); + public static java.lang.String valueOf(double); + public static java.lang.String valueOf(float); + public static java.lang.String valueOf(int); + public static java.lang.String valueOf(java.lang.Object); + public static java.lang.String valueOf(long); + public boolean contentEquals(java.lang.StringBuffer); + public boolean endsWith(java.lang.String); + public boolean equalsIgnoreCase(java.lang.String); + public boolean equals(java.lang.Object); + public boolean matches(java.lang.String); + public boolean regionMatches(boolean,int,java.lang.String,int,int); + public boolean regionMatches(int,java.lang.String,int,int); + public boolean startsWith(java.lang.String); + public boolean startsWith(java.lang.String,int); + public byte[] getBytes(); + public byte[] getBytes(java.lang.String); + public char charAt(int); + public char[] toCharArray(); + public int compareToIgnoreCase(java.lang.String); + public int compareTo(java.lang.Object); + public int compareTo(java.lang.String); + public int hashCode(); + public int indexOf(int); + public int indexOf(int,int); + public int indexOf(java.lang.String); + public int indexOf(java.lang.String,int); + public int lastIndexOf(int); + public int lastIndexOf(int,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.CharSequence subSequence(int,int); + public java.lang.String concat(java.lang.String); + public java.lang.String replaceAll(java.lang.String,java.lang.String); + public java.lang.String replace(char,char); + public java.lang.String replaceFirst(java.lang.String,java.lang.String); + public java.lang.String[] split(java.lang.String); + public java.lang.String[] split(java.lang.String,int); + public java.lang.String substring(int); + public java.lang.String substring(int,int); + public java.lang.String toLowerCase(); + public java.lang.String toLowerCase(java.util.Locale); + public java.lang.String toString(); + public java.lang.String toUpperCase(); + public java.lang.String toUpperCase(java.util.Locale); + public java.lang.String trim(); +} + +# Remove - StringBuffer method calls. Remove all invocations of StringBuffer +# methods without side effects whose return values are not used. +-assumenosideeffects public class java.lang.StringBuffer { + public java.lang.String toString(); + public char charAt(int); + public int capacity(); + public int codePointAt(int); + public int codePointBefore(int); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + 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. +-assumenosideeffects public class java.lang.StringBuilder { + public java.lang.String toString(); + public char charAt(int); + public int capacity(); + public int codePointAt(int); + public int codePointBefore(int); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + public java.lang.String substring(int,int); +} diff --git a/src/proguard/gui/package.html b/src/proguard/gui/package.html new file mode 100644 index 000000000..4eedcc271 --- /dev/null +++ b/src/proguard/gui/package.html @@ -0,0 +1,3 @@ + +This package contains a GUI for ProGuard and ReTrace. + diff --git a/src/proguard/gui/splash/BufferedSprite.java b/src/proguard/gui/splash/BufferedSprite.java new file mode 100644 index 000000000..5625acb81 --- /dev/null +++ b/src/proguard/gui/splash/BufferedSprite.java @@ -0,0 +1,145 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * This Sprite encapsulates another Sprite, which is then buffered in an Image. + * + * @author Eric Lafortune + */ +public class BufferedSprite implements 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 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 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(int bufferX, + int bufferY, + Image bufferImage, + Color backgroundColor, + Sprite sprite, + VariableInt x, + VariableInt y) + { + this.bufferX = bufferX; + this.bufferY = bufferY; + this.bufferImage = bufferImage; + this.backgroundColor = backgroundColor; + this.sprite = sprite; + this.x = x; + this.y = y; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + 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, 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, + 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 new file mode 100644 index 000000000..0443a0ca9 --- /dev/null +++ b/src/proguard/gui/splash/CircleSprite.java @@ -0,0 +1,74 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite represents an animated circle. It can optionally be filled. + * + * @author Eric Lafortune + */ +public class CircleSprite implements Sprite +{ + 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 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, + VariableInt x, + VariableInt y, + VariableInt radius) + { + this.filled = filled; + this.x = x; + this.y = y; + this.radius = radius; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + int xt = x.getInt(time); + int yt = y.getInt(time); + int r = radius.getInt(time); + + if (filled) + { + graphics.fillOval(xt - r, yt - r, 2 * r, 2 * r); + } + else + { + graphics.drawOval(xt - r, yt - r, 2 * r, 2 * r); + } + } +} diff --git a/src/proguard/gui/splash/ClipSprite.java b/src/proguard/gui/splash/ClipSprite.java new file mode 100644 index 000000000..a8ac36853 --- /dev/null +++ b/src/proguard/gui/splash/ClipSprite.java @@ -0,0 +1,85 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite encapsulates another Sprite, which is clipped by a clip Sprite. + * + * @author Eric Lafortune + */ +public class ClipSprite implements Sprite +{ + private final VariableColor insideClipColor; + private final VariableColor outsideClipColor; + private final Sprite clipSprite; + private final Sprite sprite; + + + /** + * Creates a new ClipSprite. + * @param insideClipColor the background color inside the clip sprite. + * @param outsideClipColor the background color outside the clip sprite. + * @param clipSprite the clip Sprite. + * @param sprite the clipped Sprite. + */ + public ClipSprite(VariableColor insideClipColor, + VariableColor outsideClipColor, + Sprite clipSprite, + Sprite sprite) + { + this.insideClipColor = insideClipColor; + this.outsideClipColor = outsideClipColor; + this.clipSprite = clipSprite; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + // Clear the background. + Color outsideColor = outsideClipColor.getColor(time); + Rectangle clip = graphics.getClipBounds(); + graphics.setPaintMode(); + graphics.setColor(outsideColor); + graphics.fillRect(0, 0, clip.width, clip.height); + + // Draw the sprite in XOR mode. + OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics); + Color insideColor = insideClipColor.getColor(time); + g.setOverrideXORMode(insideColor); + sprite.paint(g, time); + g.setOverrideXORMode(null); + + // Clear the clip area. + g.setOverrideColor(insideColor); + clipSprite.paint(g, time); + g.setOverrideColor(null); + + // Draw the sprite in XOR mode. + g.setOverrideXORMode(insideColor); + sprite.paint(g, time); + g.setOverrideXORMode(null); + } +} diff --git a/src/proguard/gui/splash/ColorSprite.java b/src/proguard/gui/splash/ColorSprite.java new file mode 100644 index 000000000..95153a28f --- /dev/null +++ b/src/proguard/gui/splash/ColorSprite.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite colors another given sprite. + * + * @author Eric Lafortune + */ +public class ColorSprite implements Sprite +{ + private final VariableColor color; + private final Sprite sprite; + + + /** + * Creates a new ColorSprite. + * @param color the variable color of the given sprite. + * @param sprite the sprite that will be colored and painted. + */ + public ColorSprite(VariableColor color, + Sprite sprite) + { + this.color = color; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long 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 new file mode 100644 index 000000000..68cbe356e --- /dev/null +++ b/src/proguard/gui/splash/CompositeSprite.java @@ -0,0 +1,56 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite is the composition of a list of Sprite objects. + * + * @author Eric Lafortune + */ +public class CompositeSprite implements Sprite +{ + private final Sprite[] sprites; + + + /** + * Creates a new CompositeSprite. + * @param sprites the array of Sprite objects to which the painting will + * be delegated, starting with the first element. + */ + public CompositeSprite(Sprite[] sprites) + { + this.sprites = sprites; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + // Draw the sprites. + for (int index = 0; index < sprites.length; index++) + { + sprites[index].paint(graphics, time); + } + } +} diff --git a/src/proguard/gui/splash/ConstantColor.java b/src/proguard/gui/splash/ConstantColor.java new file mode 100644 index 000000000..fa7f3a55f --- /dev/null +++ b/src/proguard/gui/splash/ConstantColor.java @@ -0,0 +1,51 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This VariableColor is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantColor implements VariableColor +{ + private final Color value; + + + /** + * Creates a new ConstantColor. + * @param value the constant value. + */ + public ConstantColor(Color value) + { + this.value = value; + } + + + // Implementation for VariableColor. + + public Color getColor(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantDouble.java b/src/proguard/gui/splash/ConstantDouble.java new file mode 100644 index 000000000..c96933246 --- /dev/null +++ b/src/proguard/gui/splash/ConstantDouble.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This VariableDouble is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantDouble implements VariableDouble +{ + private final double value; + + + /** + * Creates a new ConstantDouble. + * @param value the constant value. + */ + public ConstantDouble(double value) + { + this.value = value; + } + + + // Implementation for VariableDouble. + + public double getDouble(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantFont.java b/src/proguard/gui/splash/ConstantFont.java new file mode 100644 index 000000000..26691b902 --- /dev/null +++ b/src/proguard/gui/splash/ConstantFont.java @@ -0,0 +1,46 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This VariableFont is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantFont implements VariableFont +{ + private final Font value; + + public ConstantFont(Font value) + { + this.value = value; + } + + + // Implementation for VariableFont. + + public Font getFont(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantInt.java b/src/proguard/gui/splash/ConstantInt.java new file mode 100644 index 000000000..ff79dd0a4 --- /dev/null +++ b/src/proguard/gui/splash/ConstantInt.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This VariableInt is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantInt implements VariableInt +{ + private final int value; + + + /** + * Creates a new ConstantInt. + * @param value the constant value. + */ + public ConstantInt(int value) + { + this.value = value; + } + + + // Implementation for VariableInt. + + public int getInt(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantString.java b/src/proguard/gui/splash/ConstantString.java new file mode 100644 index 000000000..e76f1eb0f --- /dev/null +++ b/src/proguard/gui/splash/ConstantString.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This VariableString is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantString implements VariableString +{ + private final String value; + + + /** + * Creates a new ConstantString. + * @param value the constant value. + */ + public ConstantString(String value) + { + this.value = value; + } + + + // Implementation for VariableString. + + public String getString(long time) + { + return value; + } +} diff --git a/src/proguard/gui/splash/ConstantTiming.java b/src/proguard/gui/splash/ConstantTiming.java new file mode 100644 index 000000000..b6a776264 --- /dev/null +++ b/src/proguard/gui/splash/ConstantTiming.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This Timing is constant over time. + * + * @author Eric Lafortune + */ +public class ConstantTiming implements Timing +{ + private final double timing; + + + /** + * Creates a new ConstantTiming with a value of 0. + */ + public ConstantTiming() + { + this(0.0); + } + + /** + * Creates a new ConstantTiming with a given value. + * @param timing the constant value of the timing. + */ + public ConstantTiming(double timing) + { + this.timing = timing; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + return timing; + } +} diff --git a/src/proguard/gui/splash/FontSprite.java b/src/proguard/gui/splash/FontSprite.java new file mode 100644 index 000000000..42f165dd8 --- /dev/null +++ b/src/proguard/gui/splash/FontSprite.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite sets the font for another given sprite. + * + * @author Eric Lafortune + */ +public class FontSprite implements Sprite +{ + private final VariableFont font; + private final Sprite sprite; + + + /** + * 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 FontSprite(VariableFont font, + Sprite sprite) + { + this.font = font; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long 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 new file mode 100644 index 000000000..f7704f95b --- /dev/null +++ b/src/proguard/gui/splash/ImageSprite.java @@ -0,0 +1,76 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite represents an animated image. + * + * @author Eric Lafortune + */ +public class ImageSprite implements Sprite +{ + private final Image image; + private final VariableInt x; + private final VariableInt y; + private final VariableDouble scaleX; + private final VariableDouble scaleY; + + + /** + * Creates a new ImageSprite. + * @param image the Image to be painted. + * @param x the variable x-coordinate of the upper-left corner of the image. + * @param y the variable y-coordinate of the upper-left corner of the image. + * @param scaleX the variable x-scale of the image. + * @param scaleY the variable y-scale of the image. + */ + public ImageSprite(Image image, + VariableInt x, + VariableInt y, + VariableDouble scaleX, + VariableDouble scaleY) + { + this.image = image; + this.x = x; + this.y = y; + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + int xt = x.getInt(time); + int yt = y.getInt(time); + + double scale_x = scaleX.getDouble(time); + double scale_y = scaleY.getDouble(time); + + int width = (int)(image.getWidth(null) * scale_x); + int height = (int)(image.getHeight(null) * scale_y); + + graphics.drawImage(image, xt, yt, width, height, null); + } +} diff --git a/src/proguard/gui/splash/LinearColor.java b/src/proguard/gui/splash/LinearColor.java new file mode 100644 index 000000000..283a1c9a7 --- /dev/null +++ b/src/proguard/gui/splash/LinearColor.java @@ -0,0 +1,72 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This VariableColor varies linearly with respect to its Timing. + * + * @author Eric Lafortune + */ +public class LinearColor implements VariableColor +{ + private final Color fromValue; + private final Color toValue; + private final Timing timing; + + private double cachedTiming = -1.0; + private Color cachedColor; + + + /** + * Creates a new LinearColor. + * @param fromValue the value that corresponds to a timing of 0. + * @param toValue the value that corresponds to a timing of 1. + * @param timing the applied timing. + */ + public LinearColor(Color fromValue, Color toValue, Timing timing) + { + this.fromValue = fromValue; + this.toValue = toValue; + this.timing = timing; + } + + + // Implementation for VariableColor. + + public Color getColor(long time) + { + double t = timing.getTiming(time); + if (t != cachedTiming) + { + cachedTiming = t; + cachedColor = + t == 0.0 ? fromValue : + t == 1.0 ? toValue : + new Color((int)(fromValue.getRed() + t * (toValue.getRed() - fromValue.getRed())), + (int)(fromValue.getGreen() + t * (toValue.getGreen() - fromValue.getGreen())), + (int)(fromValue.getBlue() + t * (toValue.getBlue() - fromValue.getBlue()))); + } + + return cachedColor; + } +} diff --git a/src/proguard/gui/splash/LinearDouble.java b/src/proguard/gui/splash/LinearDouble.java new file mode 100644 index 000000000..bf926b013 --- /dev/null +++ b/src/proguard/gui/splash/LinearDouble.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This VariableDouble varies linearly with respect to its Timing. + * + * @author Eric Lafortune + */ +public class LinearDouble implements VariableDouble +{ + private final double fromValue; + private final double toValue; + private final Timing timing; + + + /** + * Creates a new LinearDouble. + * @param fromValue the value that corresponds to a timing of 0. + * @param toValue the value that corresponds to a timing of 1. + * @param timing the applied timing. + */ + public LinearDouble(double fromValue, double toValue, Timing timing) + { + this.fromValue = fromValue; + this.toValue = toValue; + this.timing = timing; + } + + + // Implementation for VariableDouble. + + public double getDouble(long time) + { + return fromValue + timing.getTiming(time) * (toValue - fromValue); + } +} diff --git a/src/proguard/gui/splash/LinearInt.java b/src/proguard/gui/splash/LinearInt.java new file mode 100644 index 000000000..46c35bc19 --- /dev/null +++ b/src/proguard/gui/splash/LinearInt.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This VariableColor varies linearly with respect to its Timing. + * + * @author Eric Lafortune + */ +public class LinearInt implements VariableInt +{ + private final int fromValue; + private final int toValue; + private final Timing timing; + + + /** + * Creates a new LinearInt. + * @param fromValue the value that corresponds to a timing of 0. + * @param toValue the value that corresponds to a timing of 1. + * @param timing the applied timing. + */ + public LinearInt(int fromValue, int toValue, Timing timing) + { + this.fromValue = fromValue; + this.toValue = toValue; + this.timing = timing; + } + + + // Implementation for VariableInt. + + public int getInt(long time) + { + return (int) (fromValue + timing.getTiming(time) * (toValue - fromValue)); + } +} diff --git a/src/proguard/gui/splash/LinearTiming.java b/src/proguard/gui/splash/LinearTiming.java new file mode 100644 index 000000000..c35348e02 --- /dev/null +++ b/src/proguard/gui/splash/LinearTiming.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This Timing ramps up linearly from 0 to 1 in a given time interval. + * + * @author Eric Lafortune + */ +public class LinearTiming implements Timing +{ + private final long fromTime; + private final long toTime; + + + /** + * Creates a new LinearTiming. + * @param fromTime the time at which the timing starts ramping up from 0. + * @param toTime the time at which the timing stops ramping up at 1. + */ + public LinearTiming(long fromTime, long toTime) + { + this.fromTime = fromTime; + this.toTime = toTime; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + // Compute the clamped linear interpolation. + return time <= fromTime ? 0.0 : + time >= toTime ? 1.0 : + (double)(time - fromTime) / (double)(toTime - fromTime); + } +} diff --git a/src/proguard/gui/splash/OverrideGraphics2D.java b/src/proguard/gui/splash/OverrideGraphics2D.java new file mode 100644 index 000000000..81a94292c --- /dev/null +++ b/src/proguard/gui/splash/OverrideGraphics2D.java @@ -0,0 +1,598 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; +import java.awt.RenderingHints.Key; +import java.awt.font.*; +import java.awt.geom.AffineTransform; +import java.awt.image.*; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * This Graphics2D allows to fix some basic settings (Color, Font, Paint, Stroke, + * XORMode) of a delegate Graphics2D, overriding any subsequent attempts to + * change those settings. + * + * @author Eric Lafortune + * @noinspection deprecation + */ +final class OverrideGraphics2D extends Graphics2D +{ + private final Graphics2D graphics; + + private Color overrideColor; + private Font overrideFont; + private Paint overridePaint; + private Stroke overrideStroke; + private Color overrideXORMode; + + private Color color; + private Font font; + private Paint paint; + private Stroke stroke; + + + /** + * Creates a new OverrideGraphics2D. + * @param graphics the delegate Graphics2D. + */ + public OverrideGraphics2D(Graphics2D graphics) + { + this.graphics = graphics; + this.color = graphics.getColor(); + this.font = graphics.getFont(); + this.paint = graphics.getPaint(); + this.stroke = graphics.getStroke(); + } + + + /** + * Fixes the Color of the Graphics2D. + * + * @param color the fixed Color, or null to undo the fixing. + */ + public void setOverrideColor(Color color) + { + this.overrideColor = color; + graphics.setColor(color != null ? color : this.color); + } + + /** + * Fixes the Font of the Graphics2D. + * + * @param font the fixed Font, or null to undo the fixing. + */ + public void setOverrideFont(Font font) + { + this.overrideFont = font; + graphics.setFont(font != null ? font : this.font); + } + + /** + * Fixes the Paint of the Graphics2D. + * + * @param paint the fixed Paint, or null to undo the fixing. + */ + public void setOverridePaint(Paint paint) + { + this.overridePaint = paint; + graphics.setPaint(paint != null ? paint : this.paint); + } + + /** + * Fixes the Stroke of the Graphics2D. + * + * @param stroke the fixed Stroke, or null to undo the fixing. + */ + public void setOverrideStroke(Stroke stroke) + { + this.overrideStroke = stroke; + graphics.setStroke(stroke != null ? stroke : this.stroke); + } + + /** + * Fixes the XORMode of the Graphics2D. + * + * @param color the fixed XORMode Color, or null to undo the fixing. + */ + public void setOverrideXORMode(Color color) + { + this.overrideXORMode = color; + if (color != null) + { + graphics.setXORMode(color); + } + else + { + graphics.setPaintMode(); + } + } + + + // Implementations for Graphics2D. + + public void setColor(Color color) + { + this.color = color; + if (overrideColor == null) + { + graphics.setColor(color); + } + } + + public void setFont(Font font) + { + this.font = font; + if (overrideFont == null) + { + graphics.setFont(font); + } + } + + public void setPaint(Paint paint) + { + this.paint = paint; + if (overridePaint == null) + { + graphics.setPaint(paint); + } + } + + public void setStroke(Stroke stroke) + { + this.stroke = stroke; + if (overrideStroke == null) + { + graphics.setStroke(stroke); + } + } + + public void setXORMode(Color color) + { + if (overrideXORMode == null) + { + graphics.setXORMode(color); + } + } + + public void setPaintMode() + { + if (overrideXORMode == null) + { + graphics.setPaintMode(); + } + } + + + public Color getColor() + { + return overrideColor != null ? color : graphics.getColor(); + } + + public Font getFont() + { + return overrideFont != null ? font : graphics.getFont(); + } + + public Paint getPaint() + { + return overridePaint != null ? paint : graphics.getPaint(); + } + + public Stroke getStroke() + { + return overrideStroke != null ? stroke : graphics.getStroke(); + } + + + public Graphics create() + { + OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics.create()); + g.setOverrideColor(overrideColor); + g.setOverrideFont(overrideFont); + g.setOverridePaint(overridePaint); + g.setOverrideStroke(overrideStroke); + + return g; + } + + public Graphics create(int x, int y, int width, int height) + { + OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics.create(x, y, width, height)); + g.setOverrideColor(overrideColor); + g.setOverrideFont(overrideFont); + g.setOverridePaint(overridePaint); + g.setOverrideStroke(overrideStroke); + + return g; + } + + + // Delegation for Graphics2D + + public void addRenderingHints(Map hints) + { + graphics.addRenderingHints(hints); + } + + public void clearRect(int x, int y, int width, int height) + { + graphics.clearRect(x, y, width, height); + } + + public void clip(Shape s) + { + graphics.clip(s); + } + + public void clipRect(int x, int y, int width, int height) + { + graphics.clipRect(x, y, width, height); + } + + public void copyArea(int x, int y, int width, int height, int dx, int dy) + { + graphics.copyArea(x, y, width, height, dx, dy); + } + + public void dispose() + { + graphics.dispose(); + } + + public void draw(Shape s) + { + graphics.draw(s); + } + + public void draw3DRect(int x, int y, int width, int height, boolean raised) + { + graphics.draw3DRect(x, y, width, height, raised); + } + + public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) + { + graphics.drawArc(x, y, width, height, startAngle, arcAngle); + } + + public void drawBytes(byte[] data, int offset, int length, int x, int y) + { + graphics.drawBytes(data, offset, length, x, y); + } + + public void drawChars(char[] data, int offset, int length, int x, int y) + { + graphics.drawChars(data, offset, length, x, y); + } + + public void drawGlyphVector(GlyphVector g, float x, float y) + { + graphics.drawGlyphVector(g, x, y); + } + + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) + { + return graphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); + } + + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) + { + return graphics.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) + { + return graphics.drawImage(img, x, y, width, height, bgcolor, observer); + } + + public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) + { + return graphics.drawImage(img, x, y, width, height, observer); + } + + public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) + { + return graphics.drawImage(img, x, y, bgcolor, observer); + } + + public boolean drawImage(Image img, int x, int y, ImageObserver observer) + { + return graphics.drawImage(img, x, y, observer); + } + + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) + { + return graphics.drawImage(img, xform, obs); + } + + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) + { + graphics.drawImage(img, op, x, y); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + graphics.drawLine(x1, y1, x2, y2); + } + + public void drawOval(int x, int y, int width, int height) + { + graphics.drawOval(x, y, width, height); + } + + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + graphics.drawPolygon(xPoints, yPoints, nPoints); + } + + public void drawPolygon(Polygon p) + { + graphics.drawPolygon(p); + } + + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) + { + graphics.drawPolyline(xPoints, yPoints, nPoints); + } + + public void drawRect(int x, int y, int width, int height) + { + graphics.drawRect(x, y, width, height); + } + + public void drawRenderableImage(RenderableImage img, AffineTransform xform) + { + graphics.drawRenderableImage(img, xform); + } + + public void drawRenderedImage(RenderedImage img, AffineTransform xform) + { + graphics.drawRenderedImage(img, xform); + } + + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) + { + graphics.drawRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + public void drawString(String s, float x, float y) + { + graphics.drawString(s, x, y); + } + + public void drawString(String str, int x, int y) + { + graphics.drawString(str, x, y); + } + + public void drawString(AttributedCharacterIterator iterator, float x, float y) + { + graphics.drawString(iterator, x, y); + } + + public void drawString(AttributedCharacterIterator iterator, int x, int y) + { + graphics.drawString(iterator, x, y); + } + + public boolean equals(Object obj) + { + return graphics.equals(obj); + } + + public void fill(Shape s) + { + graphics.fill(s); + } + + public void fill3DRect(int x, int y, int width, int height, boolean raised) + { + graphics.fill3DRect(x, y, width, height, raised); + } + + public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) + { + graphics.fillArc(x, y, width, height, startAngle, arcAngle); + } + + public void fillOval(int x, int y, int width, int height) + { + graphics.fillOval(x, y, width, height); + } + + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) + { + graphics.fillPolygon(xPoints, yPoints, nPoints); + } + + public void fillPolygon(Polygon p) + { + graphics.fillPolygon(p); + } + + public void fillRect(int x, int y, int width, int height) + { + graphics.fillRect(x, y, width, height); + } + + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) + { + graphics.fillRoundRect(x, y, width, height, arcWidth, arcHeight); + } + + public Color getBackground() + { + return graphics.getBackground(); + } + + public Shape getClip() + { + return graphics.getClip(); + } + + public Rectangle getClipBounds() + { + return graphics.getClipBounds(); + } + + public Rectangle getClipBounds(Rectangle r) + { + return graphics.getClipBounds(r); + } + + public Rectangle getClipRect() + { + return graphics.getClipRect(); + } + + public Composite getComposite() + { + return graphics.getComposite(); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + return graphics.getDeviceConfiguration(); + } + + public FontMetrics getFontMetrics() + { + return graphics.getFontMetrics(); + } + + public FontMetrics getFontMetrics(Font f) + { + return graphics.getFontMetrics(f); + } + + public FontRenderContext getFontRenderContext() + { + return graphics.getFontRenderContext(); + } + + public Object getRenderingHint(Key hintKey) + { + return graphics.getRenderingHint(hintKey); + } + + public RenderingHints getRenderingHints() + { + return graphics.getRenderingHints(); + } + + public AffineTransform getTransform() + { + return graphics.getTransform(); + } + + public int hashCode() + { + return graphics.hashCode(); + } + + public boolean hit(Rectangle rect, Shape s, boolean onStroke) + { + return graphics.hit(rect, s, onStroke); + } + + public boolean hitClip(int x, int y, int width, int height) + { + return graphics.hitClip(x, y, width, height); + } + + public void rotate(double theta) + { + graphics.rotate(theta); + } + + public void rotate(double theta, double x, double y) + { + graphics.rotate(theta, x, y); + } + + public void scale(double sx, double sy) + { + graphics.scale(sx, sy); + } + + public void setBackground(Color color) + { + graphics.setBackground(color); + } + + public void setClip(int x, int y, int width, int height) + { + graphics.setClip(x, y, width, height); + } + + public void setClip(Shape clip) + { + graphics.setClip(clip); + } + + public void setComposite(Composite comp) + { + graphics.setComposite(comp); + } + + public void setRenderingHint(Key hintKey, Object hintValue) + { + graphics.setRenderingHint(hintKey, hintValue); + } + + public void setRenderingHints(Map hints) + { + graphics.setRenderingHints(hints); + } + + public void setTransform(AffineTransform Tx) + { + graphics.setTransform(Tx); + } + + public void shear(double shx, double shy) + { + graphics.shear(shx, shy); + } + + public String toString() + { + return graphics.toString(); + } + + public void transform(AffineTransform Tx) + { + graphics.transform(Tx); + } + + public void translate(double tx, double ty) + { + graphics.translate(tx, ty); + } + + public void translate(int x, int y) + { + graphics.translate(x, y); + } +} diff --git a/src/proguard/gui/splash/RectangleSprite.java b/src/proguard/gui/splash/RectangleSprite.java new file mode 100644 index 000000000..547b54925 --- /dev/null +++ b/src/proguard/gui/splash/RectangleSprite.java @@ -0,0 +1,114 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite represents an animated rounded rectangle. It can optionally be filled. + * + * @author Eric Lafortune + */ +public class RectangleSprite implements Sprite +{ + 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 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, + VariableColor color, + VariableInt x, + VariableInt y, + VariableInt width, + VariableInt height) + { + 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 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, + 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; + this.height = height; + this.arcWidth = arcWidth; + this.arcHeight = arcHeight; + } + + // Implementation for 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); + int h = height.getInt(time); + int aw = arcWidth.getInt(time); + int ah = arcHeight.getInt(time); + + if (filled) + { + graphics.fillRoundRect(xt, yt, w, h, aw, ah); + } + else + { + graphics.drawRoundRect(xt, yt, w, h, aw, ah); + } + } +} diff --git a/src/proguard/gui/splash/SawToothTiming.java b/src/proguard/gui/splash/SawToothTiming.java new file mode 100644 index 000000000..393f27e64 --- /dev/null +++ b/src/proguard/gui/splash/SawToothTiming.java @@ -0,0 +1,53 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This Timing ramps up linearly from 0 to 1 in a given repeated time interval. + * + * @author Eric Lafortune + */ +public class SawToothTiming implements Timing +{ + private final long period; + private final long phase; + + + /** + * Creates a new SawToothTiming. + * @param period the time period for a full cycle. + * @param phase the phase of the cycle, which is added to the actual time. + */ + public SawToothTiming(long period, long phase) + { + this.period = period; + this.phase = phase; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + // Compute the translated and scaled saw-tooth function. + return (double)((time + phase) % period) / (double)period; + } +} diff --git a/src/proguard/gui/splash/ShadowedSprite.java b/src/proguard/gui/splash/ShadowedSprite.java new file mode 100644 index 000000000..fadca5f6e --- /dev/null +++ b/src/proguard/gui/splash/ShadowedSprite.java @@ -0,0 +1,109 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite adds a drop shadow to another Sprite. + * + * @author Eric Lafortune + */ +public class ShadowedSprite implements 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; + + + /** + * Creates a new ShadowedSprite. + * @param xOffset the variable x-offset of the shadow, relative to the sprite itself. + * @param yOffset the variable y-offset of the shadow, relative to the sprite itself. + * @param alpha the variable darkness of the shadow (between 0 and 1). + * @param blur the variable blur of the shadow (0 for sharp shadows, 1 or + * more for increasingly blurry shadows). + * @param sprite the Sprite to be painted with its shadow. + */ + public ShadowedSprite(VariableInt xOffset, + VariableInt yOffset, + VariableDouble alpha, + VariableInt blur, + Sprite sprite) + { + this.xOffset = xOffset; + this.yOffset = yOffset; + this.alpha = alpha; + this.blur = blur; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + double l = alpha.getDouble(time); + int b = blur.getInt(time) + 1; + + 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); + } + + // 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. + for (int x = 0; x < b; x++) + { + for (int y = 0; y < b; y++) + { + int xt = xo + x; + int yt = yo + y; + 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 new file mode 100644 index 000000000..ab3d0c132 --- /dev/null +++ b/src/proguard/gui/splash/SineTiming.java @@ -0,0 +1,53 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This Timing varies between 0 and 1, as a sine wave over time. + * + * @author Eric Lafortune + */ +public class SineTiming implements Timing +{ + private final long period; + private final long phase; + + + /** + * Creates a new SineTiming. + * @param period the time period for a full cycle. + * @param phase the phase of the cycle, which is added to the actual time. + */ + public SineTiming(long period, long phase) + { + this.period = period; + this.phase = phase; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + // Compute the translated and scaled sine function. + return 0.5 + 0.5 * Math.sin(2.0 * Math.PI * (time + phase) / period); + } +} diff --git a/src/proguard/gui/splash/SmoothTiming.java b/src/proguard/gui/splash/SmoothTiming.java new file mode 100644 index 000000000..a691d3ce2 --- /dev/null +++ b/src/proguard/gui/splash/SmoothTiming.java @@ -0,0 +1,66 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This Timing ramps up smoothly from 0 to 1 in a given time interval. + * + * @author Eric Lafortune + */ +public class SmoothTiming implements Timing +{ + private final long fromTime; + private final long toTime; + + + /** + * Creates a new SmoothTiming. + * @param fromTime the time at which the timing starts ramping up from 0. + * @param toTime the time at which the timing stops ramping up at 1. + */ + public SmoothTiming(long fromTime, long toTime) + { + this.fromTime = fromTime; + this.toTime = toTime; + } + + + // Implementation for Timing. + + public double getTiming(long time) + { + if (time <= fromTime) + { + return 0.0; + } + + if (time >= toTime) + { + return 1.0; + } + + // Compute the linear interpolation. + double timing = (double) (time - fromTime) / (double) (toTime - fromTime); + + // Smooth the interpolation at the ends. + return timing * timing * (3.0 - 2.0 * timing); + } +} diff --git a/src/proguard/gui/splash/SplashPanel.java b/src/proguard/gui/splash/SplashPanel.java new file mode 100644 index 000000000..af959e9f9 --- /dev/null +++ b/src/proguard/gui/splash/SplashPanel.java @@ -0,0 +1,235 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import proguard.gui.SwingUtil; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.lang.reflect.InvocationTargetException; + +/** + * This JPanel renders an animated Sprite. + * + * @author Eric Lafortune + */ +public class SplashPanel extends JPanel +{ + private final MyAnimator animator = new MyAnimator(); + private final MyRepainter repainter = new MyRepainter(); + + private final Sprite sprite; + private final double sleepFactor; + + private long startTime = Long.MAX_VALUE; + private final long stopTime; + + private volatile Thread animationThread; + + + /** + * Creates a new SplashPanel with the given Sprite, which will be animated + * indefinitely. + * @param sprite the Sprite that will be animated. + * @param processorLoad the fraction of processing time to be spend on + * animating the Sprite (between 0 and 1). + */ + public SplashPanel(Sprite sprite, double processorLoad) + { + this(sprite, processorLoad, (long)Integer.MAX_VALUE); + } + + + /** + * Creates a new SplashPanel with the given Sprite, which will be animated + * for a limited period of time. + * @param sprite the Sprite that will be animated. + * @param processorLoad the fraction of processing time to be spend on + * animating the Sprite (between 0 and 1). + * @param stopTime the number of milliseconds after which the + * animation will be stopped automatically. + */ + public SplashPanel(Sprite sprite, double processorLoad, long stopTime) + { + this.sprite = sprite; + this.sleepFactor = (1.0-processorLoad) / processorLoad; + this.stopTime = stopTime; + + // Restart the animation on a mouse click. + addMouseListener(new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + SplashPanel.this.start(); + } + }); + } + + + /** + * Starts the animation. + */ + public void start() + { + // Go to the beginning of the animation. + startTime = System.currentTimeMillis(); + + // Make sure we have an animation thread running. + if (animationThread == null) + { + animationThread = new Thread(animator); + animationThread.start(); + } + } + + + /** + * Stops the animation. + */ + public void stop() + { + // Go to the end of the animation. + startTime = 0L; + + // Let the animation thread stop itself. + animationThread = null; + + // Repaint the SplashPanel one last time. + try + { + SwingUtil.invokeAndWait(repainter); + } + catch (InterruptedException ex) + { + // Nothing. + } + catch (InvocationTargetException ex) + { + // Nothing. + } + } + + + // Implementation for JPanel. + + public void paintComponent(Graphics graphics) + { + super.paintComponent(graphics); + + sprite.paint(graphics, System.currentTimeMillis() - startTime); + } + + + /** + * This Runnable makes sure its SplashPanel gets repainted regularly, + * depending on the targeted processor load. + */ + private class MyAnimator implements Runnable + { + public void run() + { + try + { + while (animationThread != null) + { + // Check if we should stop the animation. + long time = System.currentTimeMillis(); + if (time > startTime + stopTime) + { + animationThread = null; + } + + // Do a repaint and time it. + SwingUtil.invokeAndWait(repainter); + + // Sleep for a proportional while. + long repaintTime = System.currentTimeMillis() - time; + long sleepTime = (long)(sleepFactor * repaintTime); + if (sleepTime < 10L) + { + sleepTime = 10L; + } + + Thread.sleep(sleepTime); + } + } + catch (InterruptedException ex) + { + // Nothing. + } + catch (InvocationTargetException ex) + { + // Nothing. + } + } + } + + + /** + * This Runnable repaints its SplashPanel. + */ + private class MyRepainter implements Runnable + { + public void run() + { + SplashPanel.this.repaint(); + } + } + + + /** + * A main method for testing the splash panel. + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setTitle("Animation"); + frame.setSize(800, 600); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension frameSize = frame.getSize(); + frame.setLocation((screenSize.width - frameSize.width) / 2, + (screenSize.height - frameSize.height) / 2); + + Sprite sprite = + new ClipSprite( + new ConstantColor(Color.white), + new ConstantColor(Color.lightGray), + new CircleSprite(true, + 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 ConstantInt(200), + new ConstantInt(300))))); + + SplashPanel panel = new SplashPanel(sprite, 0.5); + panel.setBackground(Color.white); + + frame.getContentPane().add(panel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + + panel.start(); + } +} diff --git a/src/proguard/gui/splash/Sprite.java b/src/proguard/gui/splash/Sprite.java new file mode 100644 index 000000000..f1f6e72c3 --- /dev/null +++ b/src/proguard/gui/splash/Sprite.java @@ -0,0 +1,41 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This interface describes objects that can paint themselves, possibly varying + * as a function of time. + * + * @author Eric Lafortune + */ +public interface Sprite +{ + /** + * Paints the object. + * + * @param graphics the Graphics to paint on. + * @param time the time since the start of the animation, expressed in + * milliseconds. + */ + public void paint(Graphics graphics, long time); +} diff --git a/src/proguard/gui/splash/TextSprite.java b/src/proguard/gui/splash/TextSprite.java new file mode 100644 index 000000000..69bd4c768 --- /dev/null +++ b/src/proguard/gui/splash/TextSprite.java @@ -0,0 +1,89 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite represents a text. + * + * @author Eric Lafortune + */ +public class TextSprite implements Sprite +{ + 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 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, + VariableInt x, + VariableInt y) + { + this(new VariableString[] { text }, new ConstantInt(0), x, y); + } + + + /** + * 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 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 + * first line of text. + */ + public TextSprite(VariableString[] text, + VariableInt spacing, + VariableInt x, + VariableInt y) + { + + this.text = text; + this.spacing = spacing; + this.x = x; + this.y = y; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + + int xt = x.getInt(time); + int yt = y.getInt(time); + + int spacingt = spacing.getInt(time); + + for (int index = 0; index < text.length; index++) + { + 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 new file mode 100644 index 000000000..dd292adf6 --- /dev/null +++ b/src/proguard/gui/splash/TimeSwitchSprite.java @@ -0,0 +1,75 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This Sprite displays another Sprite in a given time interval. + * The time of the encapsulated Sprite is shifted by the start time. + * + * @author Eric Lafortune + */ +public class TimeSwitchSprite implements 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. + * @param onTime the start time. + * @param sprite the toggled Sprite. + */ + public TimeSwitchSprite(long onTime, Sprite sprite) + { + this(onTime, 0L, sprite); + } + + + /** + * Creates a new TimeSwitchSprite for displaying a given Sprite in a given + * time interval. + * @param onTime the start time. + * @param offTime the stop time. + * @param sprite the toggled Sprite. + */ + public TimeSwitchSprite(long onTime, long offTime, Sprite sprite) + { + this.onTime = onTime; + this.offTime = offTime; + this.sprite = sprite; + } + + + // Implementation for Sprite. + + public void paint(Graphics graphics, long time) + { + 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 new file mode 100644 index 000000000..bb159006d --- /dev/null +++ b/src/proguard/gui/splash/Timing.java @@ -0,0 +1,34 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This interface maps a time to a normalized timing between 0 and 1. + * + * @author Eric Lafortune + */ +interface Timing +{ + /** + * Returns the timing for the given time. + */ + public double getTiming(long time); +} diff --git a/src/proguard/gui/splash/TypeWriterString.java b/src/proguard/gui/splash/TypeWriterString.java new file mode 100644 index 000000000..d304dcab7 --- /dev/null +++ b/src/proguard/gui/splash/TypeWriterString.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This VariableString produces a String that grows linearly with respect to its + * Timing, as if it is being written on a typewriter. A cursor at the end + * precedes the typed characters. + * + * @author Eric Lafortune + */ +public class TypeWriterString implements VariableString +{ + private final String string; + private final Timing timing; + + private int cachedLength = -1; + private String cachedString; + + + /** + * Creates a new TypeWriterString. + * @param string the basic String. + * @param timing the applied timing. + */ + public TypeWriterString(String string, Timing timing) + { + this.string = string; + this.timing = timing; + } + + + // Implementation for VariableString. + + public String getString(long time) + { + double t = timing.getTiming(time); + + int stringLength = string.length(); + int length = (int)(stringLength * t + 0.5); + if (length != cachedLength) + { + cachedLength = length; + cachedString = string.substring(0, length); + if (t > 0.0 && length < stringLength) + { + cachedString += "_"; + } + } + + return cachedString; + } +} diff --git a/src/proguard/gui/splash/VariableColor.java b/src/proguard/gui/splash/VariableColor.java new file mode 100644 index 000000000..ce573aa1e --- /dev/null +++ b/src/proguard/gui/splash/VariableColor.java @@ -0,0 +1,36 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This interface represents a Color that varies with time. + * + * @author Eric Lafortune + */ +interface VariableColor +{ + /** + * Returns the Color for the given time. + */ + public Color getColor(long time); +} diff --git a/src/proguard/gui/splash/VariableDouble.java b/src/proguard/gui/splash/VariableDouble.java new file mode 100644 index 000000000..73174036e --- /dev/null +++ b/src/proguard/gui/splash/VariableDouble.java @@ -0,0 +1,34 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This interface represents a double that varies with time. + * + * @author Eric Lafortune + */ +interface VariableDouble +{ + /** + * Returns the double for the given time. + */ + public double getDouble(long time); +} diff --git a/src/proguard/gui/splash/VariableFont.java b/src/proguard/gui/splash/VariableFont.java new file mode 100644 index 000000000..0668c38f5 --- /dev/null +++ b/src/proguard/gui/splash/VariableFont.java @@ -0,0 +1,36 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This interface represents a Font that varies with time. + * + * @author Eric Lafortune + */ +interface VariableFont +{ + /** + * Returns the Font for the given time. + */ + public Font getFont(long time); +} diff --git a/src/proguard/gui/splash/VariableInt.java b/src/proguard/gui/splash/VariableInt.java new file mode 100644 index 000000000..b4523ef15 --- /dev/null +++ b/src/proguard/gui/splash/VariableInt.java @@ -0,0 +1,34 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This interface represents an integer that varies with time. + * + * @author Eric Lafortune + */ +interface VariableInt +{ + /** + * Returns the integer for the given time. + */ + public int getInt(long time); +} diff --git a/src/proguard/gui/splash/VariableSizeFont.java b/src/proguard/gui/splash/VariableSizeFont.java new file mode 100644 index 000000000..52b8ab26a --- /dev/null +++ b/src/proguard/gui/splash/VariableSizeFont.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +import java.awt.*; + +/** + * This VariableFont varies in size with respect to its Timing. + * + * @author Eric Lafortune + */ +public class VariableSizeFont implements VariableFont +{ + private final Font font; + private final VariableDouble size; + + private float cachedSize = -1.0f; + private Font cachedFont; + + + /** + * Creates a new VariableSizeFont + * @param font the base font. + * @param size the variable size of the font. + */ + public VariableSizeFont(Font font, VariableDouble size) + { + this.font = font; + this.size = size; + } + + + // Implementation for VariableFont. + + public Font getFont(long time) + { + float s = (float)size.getDouble(time); + + if (s != cachedSize) + { + cachedSize = s; + cachedFont = font.deriveFont((float)s); + } + + return cachedFont; + } +} diff --git a/src/proguard/gui/splash/VariableString.java b/src/proguard/gui/splash/VariableString.java new file mode 100644 index 000000000..81ca8f6ea --- /dev/null +++ b/src/proguard/gui/splash/VariableString.java @@ -0,0 +1,34 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.splash; + +/** + * This interface represents a String that varies with time. + * + * @author Eric Lafortune + */ +interface VariableString +{ + /** + * Returns the String for the given time. + */ + public String getString(long time); +} diff --git a/src/proguard/gui/splash/package.html b/src/proguard/gui/splash/package.html new file mode 100644 index 000000000..209fad7c1 --- /dev/null +++ b/src/proguard/gui/splash/package.html @@ -0,0 +1,4 @@ + +This package contains a library for creating splash screens and animations +with text, graphical elements, and some special effects. + diff --git a/src/proguard/gui/vtitle.png b/src/proguard/gui/vtitle.png new file mode 100644 index 0000000000000000000000000000000000000000..218f71620e226a698a2b9d0b7b66cfc20ffad025 GIT binary patch literal 23313 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ξ*jCU1KT)IrYn(N#Uit~|(^b8vlbRNu^gS&o7$ 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 literal 0 HcmV?d00001 diff --git a/src/proguard/io/CascadingDataEntryWriter.java b/src/proguard/io/CascadingDataEntryWriter.java new file mode 100644 index 000000000..62f3bf701 --- /dev/null +++ b/src/proguard/io/CascadingDataEntryWriter.java @@ -0,0 +1,94 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; + +/** + * This DataEntryWriter delegates to a given DataEntryWriter, or failing that, + * to another given DataEntryWriter. + * + * @author Eric Lafortune + */ +public class CascadingDataEntryWriter implements DataEntryWriter +{ + private DataEntryWriter dataEntryWriter1; + private DataEntryWriter dataEntryWriter2; + + + /** + * 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 + * delegated, if the first one can't provide an + * output stream. + */ + public CascadingDataEntryWriter(DataEntryWriter dataEntryWriter1, + DataEntryWriter dataEntryWriter2) + { + this.dataEntryWriter1 = dataEntryWriter1; + this.dataEntryWriter2 = dataEntryWriter2; + } + + + // Implementations for DataEntryWriter. + + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + // Try to create a directory with the first data entry writer, or + // otherwise with the second data entry writer. + return dataEntryWriter1.createDirectory(dataEntry) || + dataEntryWriter2.createDirectory(dataEntry); + } + + + public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + { + return getOutputStream(dataEntry, null); + } + + + public OutputStream getOutputStream(DataEntry dataEntry, + Finisher finisher) throws IOException + { + // Try to get an output stream from the first data entry writer. + OutputStream outputStream = + dataEntryWriter1.getOutputStream(dataEntry, finisher); + + // Return it, if it's not null. Otherwise try to get an output stream + // from the second data entry writer. + return outputStream != null ? + outputStream : + dataEntryWriter2.getOutputStream(dataEntry, finisher); + } + + + public void close() throws IOException + { + dataEntryWriter1.close(); + dataEntryWriter2.close(); + + dataEntryWriter1 = null; + dataEntryWriter2 = null; + } +} diff --git a/src/proguard/io/ClassFilter.java b/src/proguard/io/ClassFilter.java new file mode 100644 index 000000000..aabc5bac8 --- /dev/null +++ b/src/proguard/io/ClassFilter.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; +import proguard.util.ExtensionMatcher; + +import java.io.IOException; + + +/** + * This DataEntryReader delegates to one of two other DataEntryReader instances, + * depending on the extension of the data entry. + * + * @author Eric Lafortune + */ +public class ClassFilter extends FilteredDataEntryReader +{ + /** + * Creates a new ClassFilter that delegates reading classes to the + * given reader. + */ + public ClassFilter(DataEntryReader classReader) + { + this(classReader, null); + } + + + /** + * Creates a new ClassFilter that delegates to either of the two given + * readers. + */ + public ClassFilter(DataEntryReader classReader, + DataEntryReader dataEntryReader) + { + super(new DataEntryNameFilter( + new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)), + classReader, + dataEntryReader); + } +} diff --git a/src/proguard/io/ClassReader.java b/src/proguard/io/ClassReader.java new file mode 100644 index 000000000..0ad382ba4 --- /dev/null +++ b/src/proguard/io/ClassReader.java @@ -0,0 +1,115 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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. + *

+ * Class files are read as ProgramClass objects or LibraryClass objects, + * depending on the isLibrary flag. + *

+ * In case of libraries, only public classes are considered, if the + * skipNonPublicLibraryClasses 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(className, + "Warning: class [" + dataEntry.getName() + "] unexpectedly contains class [" + ClassUtil.externalClassName(className) + "]"); + } + + clazz.accept(classVisitor); + } + + dataEntry.closeInputStream(); + } + catch (Exception ex) + { + throw (IOException)new IOException("Can't process class ["+dataEntry.getName()+"] ("+ex.getMessage()+")").initCause(ex); + } + } +} diff --git a/src/proguard/io/ClassRewriter.java b/src/proguard/io/ClassRewriter.java new file mode 100644 index 000000000..97e8aef56 --- /dev/null +++ b/src/proguard/io/ClassRewriter.java @@ -0,0 +1,80 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ProgramClassWriter; + +import java.io.*; + + +/** + * This DataEntryReader reads class entries and writes their corresponding + * versions from the ClassPool to a given DataEntryWriter. + * + * @author Eric Lafortune + */ +public class ClassRewriter implements DataEntryReader +{ + private final ClassPool classPool; + private final DataEntryWriter dataEntryWriter; + + + public ClassRewriter(ClassPool classPool, + DataEntryWriter dataEntryWriter) + { + this.classPool = classPool; + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryReader. + + public void read(DataEntry dataEntry) throws IOException + { + String inputName = dataEntry.getName(); + String className = inputName.substring(0, inputName.length() - ClassConstants.CLASS_FILE_EXTENSION.length()); + + // Find the modified class corrsponding to the input entry. + ProgramClass programClass = (ProgramClass)classPool.getClass(className); + if (programClass != null) + { + // Rename the data entry if necessary. + String newClassName = programClass.getName(); + if (!className.equals(newClassName)) + { + dataEntry = new RenamedDataEntry(dataEntry, newClassName + ClassConstants.CLASS_FILE_EXTENSION); + } + + // Get the output entry corresponding to this input entry. + OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry); + if (outputStream != null) + { + // Write the class to the output entry. + DataOutputStream classOutputStream = new DataOutputStream(outputStream); + + new ProgramClassWriter(classOutputStream).visitProgramClass(programClass); + + classOutputStream.flush(); + } + } + } +} diff --git a/src/proguard/io/DataEntry.java b/src/proguard/io/DataEntry.java new file mode 100644 index 000000000..681331bfc --- /dev/null +++ b/src/proguard/io/DataEntry.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; + +/** + * This interface describes a data entry, e.g. a ZIP entry, a file, or a + * directory. + * + * @author Eric Lafortune + */ +public interface DataEntry +{ + /** + * Returns the name of this data entry. + */ + public String getName(); + + /** + * Returns whether the data entry represents a directory. + */ + public boolean isDirectory(); + + + /** + * Returns an input stream for reading the content of this data entry. + * The data entry may not represent a directory. + */ + public InputStream getInputStream() throws IOException; + + + /** + * Closes the previously retrieved InputStream. + */ + public void closeInputStream() throws IOException; + + + /** + * Returns the parent of this data entry, or null if it doesn't + * have one. + */ + public DataEntry getParent(); +} diff --git a/src/proguard/io/DataEntryClassWriter.java b/src/proguard/io/DataEntryClassWriter.java new file mode 100644 index 000000000..e9f0327cb --- /dev/null +++ b/src/proguard/io/DataEntryClassWriter.java @@ -0,0 +1,85 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +import java.io.*; + +/** + * This ClassVisitor writes out the ProgramClass objects that it visits to the + * given DataEntry, modified to have the correct name. + * + * @author Eric Lafortune + */ +public class DataEntryClassWriter +extends SimplifiedVisitor +implements ClassVisitor +{ + private final DataEntryWriter dataEntryWriter; + private final DataEntry templateDataEntry; + + + /** + * Creates a new DataEntryClassWriter for writing to the given + * DataEntryWriter, based on the given template DataEntry. + */ + public DataEntryClassWriter(DataEntryWriter dataEntryWriter, + DataEntry templateDataEntry) + { + this.dataEntryWriter = dataEntryWriter; + this.templateDataEntry = templateDataEntry; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Rename the data entry if necessary. + String actualClassName = programClass.getName(); + DataEntry actualDataEntry = + new RenamedDataEntry(templateDataEntry, + actualClassName + ClassConstants.CLASS_FILE_EXTENSION); + + try + { + // Get the output entry corresponding to this input entry. + OutputStream outputStream = dataEntryWriter.getOutputStream(actualDataEntry); + if (outputStream != null) + { + // Write the class to the output entry. + DataOutputStream classOutputStream = new DataOutputStream(outputStream); + + new ProgramClassWriter(classOutputStream).visitProgramClass(programClass); + + classOutputStream.flush(); + } + } + catch (IOException e) + { + throw new RuntimeException("Can't write program class ["+actualClassName+"] to ["+actualDataEntry+"] ("+e.getMessage()+")", e); + } + } +} diff --git a/src/proguard/io/DataEntryCopier.java b/src/proguard/io/DataEntryCopier.java new file mode 100644 index 000000000..f63e3e888 --- /dev/null +++ b/src/proguard/io/DataEntryCopier.java @@ -0,0 +1,247 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.io; + +import proguard.util.ExtensionMatcher; + +import java.io.*; + + +/** + * This DataEntryReader writes the ZIP entries and files that it reads to a + * given DataEntryWriter. + * + * @author Eric Lafortune + */ +public class DataEntryCopier implements DataEntryReader +{ + private static final int BUFFER_SIZE = 1024; + + private final DataEntryWriter dataEntryWriter; + private final byte[] buffer = new byte[BUFFER_SIZE]; + + + + public DataEntryCopier(DataEntryWriter dataEntryWriter) + { + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryReader. + + public void read(DataEntry dataEntry) throws IOException + { + try + { + if (dataEntry.isDirectory()) + { + dataEntryWriter.createDirectory(dataEntry); + } + else + { + // Get the output entry corresponding to this input entry. + OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry); + if (outputStream != null) + { + InputStream inputStream = dataEntry.getInputStream(); + + // Copy the data from the input entry to the output entry. + copyData(inputStream, outputStream); + + // Close the data entries. + dataEntry.closeInputStream(); + } + } + } + catch (IOException ex) + { + System.err.println("Warning: can't write resource [" + dataEntry.getName() + "] (" + ex.getMessage() + ")"); + } + } + + + /** + * Copies all data that it can read from the given input stream to the + * given output stream. + */ + protected void copyData(InputStream inputStream, + OutputStream outputStream) + throws IOException + { + while (true) + { + int count = inputStream.read(buffer); + if (count < 0) + { + break; + } + outputStream.write(buffer, 0, count); + } + + outputStream.flush(); + } + + + /** + * A main method for testing file/jar/war/directory copying. + */ + public static void main(String[] args) + { + try + { + String input = args[0]; + String output = args[1]; + + boolean outputIsJar = output.endsWith(".jar"); + boolean outputIsWar = output.endsWith(".war"); + boolean outputIsEar = output.endsWith(".ear"); + boolean outputIsZip = output.endsWith(".zip"); + + DataEntryWriter writer = new DirectoryWriter(new File(output), + outputIsJar || + outputIsWar || + outputIsEar || + outputIsZip); + + if (!outputIsJar) + { + // Zip up any zips, if necessary. + DataEntryWriter zipWriter = new JarWriter(writer); + if (outputIsZip) + { + // Always zip. + writer = zipWriter; + } + else + { + // Only zip up zips. + writer = new FilteredDataEntryWriter(new DataEntryParentFilter( + new DataEntryNameFilter( + new ExtensionMatcher(".zip"))), + zipWriter, + writer); + } + + // Zip up any wars, if necessary. + DataEntryWriter warWriter = new JarWriter(writer); + if (outputIsWar) + { + // Always zip. + writer = warWriter; + } + else + { + // Only zip up wars. + writer = new FilteredDataEntryWriter(new DataEntryParentFilter( + new DataEntryNameFilter( + new ExtensionMatcher(".war"))), + warWriter, + writer); + } + } + + // Zip up any jars, if necessary. + DataEntryWriter jarWriter = new JarWriter(writer); + if (outputIsJar) + { + // Always zip. + writer = jarWriter; + } + else + { + // Only zip up jars. + writer = new FilteredDataEntryWriter(new DataEntryParentFilter( + new DataEntryNameFilter( + new ExtensionMatcher(".jar"))), + jarWriter, + writer); + } + + + // Create the copying DataEntryReader. + DataEntryReader reader = new DataEntryCopier(writer); + + + boolean inputIsJar = input.endsWith(".jar"); + boolean inputIsWar = input.endsWith(".war"); + boolean inputIsZip = input.endsWith(".zip"); + + // Unzip any jars, if necessary. + DataEntryReader jarReader = new JarReader(reader); + if (inputIsJar) + { + // Always unzip. + reader = jarReader; + } + else + { + // Only unzip jar entries. + reader = new FilteredDataEntryReader(new DataEntryNameFilter( + new ExtensionMatcher(".jar")), + jarReader, + reader); + + // Unzip any wars, if necessary. + DataEntryReader warReader = new JarReader(reader); + if (inputIsWar) + { + // Always unzip. + reader = warReader; + } + else + { + // Only unzip war entries. + reader = new FilteredDataEntryReader(new DataEntryNameFilter( + new ExtensionMatcher(".war")), + warReader, + reader); + } + + // Unzip any zips, if necessary. + DataEntryReader zipReader = new JarReader(reader); + if (inputIsZip) + { + // Always unzip. + reader = zipReader; + } + else + { + // Only unzip zip entries. + reader = new FilteredDataEntryReader(new DataEntryNameFilter( + new ExtensionMatcher(".zip")), + zipReader, + reader); + } + } + + DirectoryPump directoryReader = new DirectoryPump(new File(input)); + + directoryReader.pumpDataEntries(reader); + + writer.close(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/io/DataEntryDirectoryFilter.java b/src/proguard/io/DataEntryDirectoryFilter.java new file mode 100644 index 000000000..5bf46bbce --- /dev/null +++ b/src/proguard/io/DataEntryDirectoryFilter.java @@ -0,0 +1,40 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.io; + +import proguard.util.StringMatcher; + +/** + * This DataEntryFilter filters data entries based on whether they represent + * directories. + * + * @author Eric Lafortune + */ +public class DataEntryDirectoryFilter +implements DataEntryFilter +{ + // Implementations for DataEntryFilter. + + public boolean accepts(DataEntry dataEntry) + { + return dataEntry != null && dataEntry.isDirectory(); + } +} \ No newline at end of file diff --git a/src/proguard/io/DataEntryFilter.java b/src/proguard/io/DataEntryFilter.java new file mode 100644 index 000000000..b8b6b2078 --- /dev/null +++ b/src/proguard/io/DataEntryFilter.java @@ -0,0 +1,38 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + +/** + * This interface provides a method to filter data entries. + * + * @author Eric Lafortune + */ +public interface DataEntryFilter +{ + /** + * Checks whether the filter accepts the given data entry. + * @param dataEntry the data entry to filter. + * @return a boolean indicating whether the filter accepts the given data + * entry. + */ + public boolean accepts(DataEntry dataEntry); +} diff --git a/src/proguard/io/DataEntryNameFilter.java b/src/proguard/io/DataEntryNameFilter.java new file mode 100644 index 000000000..5aebcddaa --- /dev/null +++ b/src/proguard/io/DataEntryNameFilter.java @@ -0,0 +1,54 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.io; + +import proguard.util.StringMatcher; + +/** + * This DataEntryFilter filters data entries based on whether their names match + * a given StringMatcher. + * + * @author Eric Lafortune + */ +public class DataEntryNameFilter +implements DataEntryFilter +{ + private final StringMatcher stringMatcher; + + + /** + * Creates a new DataEntryNameFilter. + * @param stringMatcher the string matcher that will be applied to the names + * of the filtered data entries. + */ + public DataEntryNameFilter(StringMatcher stringMatcher) + { + this.stringMatcher = stringMatcher; + } + + + // Implementations for DataEntryFilter. + + public boolean accepts(DataEntry dataEntry) + { + return dataEntry != null && stringMatcher.matches(dataEntry.getName()); + } +} diff --git a/src/proguard/io/DataEntryObfuscator.java b/src/proguard/io/DataEntryObfuscator.java new file mode 100644 index 000000000..c5742e98e --- /dev/null +++ b/src/proguard/io/DataEntryObfuscator.java @@ -0,0 +1,150 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassUtil; + +import java.io.IOException; +import java.util.Map; + +/** + * This DataEntryReader delegates to another DataEntryReader, renaming the + * data entries based on the renamed classes in the given ClassPool. + * + * @author Eric Lafortune + */ +public class DataEntryObfuscator implements DataEntryReader +{ + private final ClassPool classPool; + private final Map packagePrefixMap; + private final DataEntryReader dataEntryReader; + + + /** + * Creates a new DataEntryObfuscator. + * @param classPool the class pool that maps from old names to new + * names. + * @param packagePrefixMap the map from old package prefixes to new package + * prefixes. + * @param dataEntryReader the DataEntryReader to which calls will be + * delegated. + */ + public DataEntryObfuscator(ClassPool classPool, + Map packagePrefixMap, + DataEntryReader dataEntryReader) + { + this.classPool = classPool; + this.packagePrefixMap = packagePrefixMap; + this.dataEntryReader = dataEntryReader; + } + + + // Implementations for DataEntryReader. + + public void read(DataEntry dataEntry) throws IOException + { + // Delegate to the actual data entry reader. + dataEntryReader.read(renamedDataEntry(dataEntry)); + } + + + /** + * Create a renamed data entry, if possible. + */ + private DataEntry renamedDataEntry(DataEntry dataEntry) + { + String dataEntryName = dataEntry.getName(); + + // Try to find a corresponding class name by removing increasingly + // long suffixes. + for (int suffixIndex = dataEntryName.length() - 1; + suffixIndex > 0; + suffixIndex--) + { + char c = dataEntryName.charAt(suffixIndex); + if (!Character.isLetterOrDigit(c)) + { + // Chop off the suffix. + String className = dataEntryName.substring(0, suffixIndex); + + // Did we get to the package separator? + if (c == ClassConstants.INTERNAL_PACKAGE_SEPARATOR) + { + break; + } + + // Is there a class corresponding to the data entry? + Clazz clazz = classPool.getClass(className); + if (clazz != null) + { + // Did the class get a new name? + String newClassName = clazz.getName(); + if (!className.equals(newClassName)) + { + // Return a renamed data entry. + String newDataEntryName = + newClassName + dataEntryName.substring(suffixIndex); + + return new RenamedDataEntry(dataEntry, newDataEntryName); + } + else + { + // Otherwise stop looking. + return dataEntry; + } + } + } + } + + // Try to find a corresponding package name by increasingly removing + // more subpackages. + String packagePrefix = dataEntryName; + do + { + // Chop off the class name or the last subpackage name. + packagePrefix = ClassUtil.internalPackagePrefix(packagePrefix); + + // Is there a package corresponding to the package prefix? + String newPackagePrefix = (String)packagePrefixMap.get(packagePrefix); + if (newPackagePrefix != null) + { + // Did the package get a new name? + if (!packagePrefix.equals(newPackagePrefix)) + { + // Return a renamed data entry. + String newDataEntryName = + newPackagePrefix + dataEntryName.substring(packagePrefix.length()); + + return new RenamedDataEntry(dataEntry, newDataEntryName); + } + else + { + // Otherwise stop looking. + return dataEntry; + } + } + } + while (packagePrefix.length() > 0); + + return dataEntry; + } +} diff --git a/src/proguard/io/DataEntryParentFilter.java b/src/proguard/io/DataEntryParentFilter.java new file mode 100644 index 000000000..1cc199778 --- /dev/null +++ b/src/proguard/io/DataEntryParentFilter.java @@ -0,0 +1,51 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This DataEntryFilter delegates filtering to a DataEntryFilter for its parent. + * + * @author Eric Lafortune + */ +public class DataEntryParentFilter +implements DataEntryFilter +{ + private final DataEntryFilter dataEntryFilter; + + + /** + * Creates a new ParentFilter. + * @param dataEntryFilter the filter that will be applied to the data + * entry's parent. + */ + public DataEntryParentFilter(DataEntryFilter dataEntryFilter) + { + this.dataEntryFilter = dataEntryFilter; + } + + + // Implementations for DataEntryFilter. + + public boolean accepts(DataEntry dataEntry) + { + return dataEntry != null && dataEntryFilter.accepts(dataEntry.getParent()); + } +} diff --git a/src/proguard/io/DataEntryPump.java b/src/proguard/io/DataEntryPump.java new file mode 100644 index 000000000..ddf946b96 --- /dev/null +++ b/src/proguard/io/DataEntryPump.java @@ -0,0 +1,43 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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 classes, + * or copy the resource files that are presented. + * + * @author Eric Lafortune + */ +public interface DataEntryPump +{ + /** + * Applies the given DataEntryReader to all data entries that the + * implementation can provide. + */ + public void pumpDataEntries(DataEntryReader dataEntryReader) + throws IOException; +} diff --git a/src/proguard/io/DataEntryReader.java b/src/proguard/io/DataEntryReader.java new file mode 100644 index 000000000..39dc82db0 --- /dev/null +++ b/src/proguard/io/DataEntryReader.java @@ -0,0 +1,38 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.IOException; + + +/** + * This interface provides methods for reading data entries. The implementation + * determines what to do with the read data, if anything. + * + * @author Eric Lafortune + */ +public interface DataEntryReader +{ + /** + * Reads the given data entry. + */ + public void read(DataEntry dataEntry) throws IOException; +} diff --git a/src/proguard/io/DataEntryRenamer.java b/src/proguard/io/DataEntryRenamer.java new file mode 100644 index 000000000..99600aeca --- /dev/null +++ b/src/proguard/io/DataEntryRenamer.java @@ -0,0 +1,104 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +import java.io.IOException; +import java.util.Map; + +/** + * This DataEntryReader delegates to another DataEntryReader, renaming the + * data entries based on the given map. Entries whose name does not appear + * in the map may be passed to an alternative DataEntryReader. + * + * @author Eric Lafortune + */ +public class DataEntryRenamer implements DataEntryReader +{ + private final Map nameMap; + private final DataEntryReader renamedDataEntryReader; + private final DataEntryReader missingDataEntryReader; + + + /** + * Creates a new DataEntryRenamer. + * @param nameMap the map from old names to new names. + * @param renamedDataEntryReader the DataEntryReader to which renamed data + * entries will be passed. + */ + public DataEntryRenamer(Map nameMap, + DataEntryReader renamedDataEntryReader) + { + this(nameMap, renamedDataEntryReader, null); + } + + + /** + * Creates a new DataEntryRenamer. + * @param nameMap the map from old names to new names. + * @param renamedDataEntryReader the DataEntryReader to which renamed data + * entries will be passed. + * @param missingDataEntryReader the optional DataEntryReader to which data + * entries that can't be renamed will be + * passed. + */ + public DataEntryRenamer(Map nameMap, + DataEntryReader renamedDataEntryReader, + DataEntryReader missingDataEntryReader) + { + this.nameMap = nameMap; + this.renamedDataEntryReader = renamedDataEntryReader; + this.missingDataEntryReader = missingDataEntryReader; + } + + + // Implementations for DataEntryReader. + + public void read(DataEntry dataEntry) throws IOException + { + String name = dataEntry.getName(); + + // Add a directory separator if necessary. + if (dataEntry.isDirectory() && + name.length() > 0) + { + name += ClassConstants.INTERNAL_PACKAGE_SEPARATOR; + } + + String newName = (String)nameMap.get(name); + if (newName != null) + { + // Remove the directory separator if necessary. + if (dataEntry.isDirectory() && + newName.length() > 0) + { + newName = newName.substring(0, newName.length() - 1); + } + + renamedDataEntryReader.read(new RenamedDataEntry(dataEntry, newName)); + } + else if (missingDataEntryReader != null) + { + missingDataEntryReader.read(dataEntry); + } + } +} diff --git a/src/proguard/io/DataEntryRewriter.java b/src/proguard/io/DataEntryRewriter.java new file mode 100644 index 000000000..ec0776710 --- /dev/null +++ b/src/proguard/io/DataEntryRewriter.java @@ -0,0 +1,148 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 extends DataEntryCopier +{ + private final ClassPool classPool; + + + /** + * Creates a new DataEntryRewriter. + */ + public DataEntryRewriter(ClassPool classPool, + DataEntryWriter dataEntryWriter) + { + super(dataEntryWriter); + + this.classPool = classPool; + } + + + // Implementations for DataEntryCopier. + + protected void copyData(InputStream inputStream, + OutputStream outputStream) + throws IOException + { + Reader reader = new BufferedReader(new InputStreamReader(inputStream)); + Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream)); + + copyData(reader, writer); + + writer.flush(); + outputStream.flush(); + } + + + /** + * Copies all data that it can read from the given reader to the given + * writer. + */ + protected void copyData(Reader reader, + Writer writer) + throws IOException + { + 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()); + } + + + // Small utility methods. + + /** + * 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 new file mode 100644 index 000000000..871f823bf --- /dev/null +++ b/src/proguard/io/DataEntryWriter.java @@ -0,0 +1,73 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; + + +/** + * This interface provides methods for writing data entries, such as ZIP entries + * or files. The implementation determines to which type of data entry the + * data will be written. + * + * @author Eric Lafortune + */ +public interface DataEntryWriter +{ + /** + * Creates a directory. + * @param dataEntry the data entry for which the directory is to be created. + * @return whether the directory has been created. + */ + public boolean createDirectory(DataEntry dataEntry) throws IOException; + + + /** + * Returns an output stream for writing data. The caller must not close + * the output stream; closing the output stream is the responsibility of + * the implementation of this interface. + * @param dataEntry the data entry for which the output stream is to be created. + * @return the output stream. The stream may be null to indicate + * that the data entry should not be written. + */ + public OutputStream getOutputStream(DataEntry dataEntry) throws IOException; + + + /** + * Returns an output stream for writing data. The caller must not close + * the output stream; closing the output stream is the responsibility of + * the implementation of this interface. + * @param dataEntry the data entry for which the output stream is to be created. + * @param finisher the optional finisher that will be called before this + * class closes the output stream (at some later point in + * time) that will be returned (now). + * @return the output stream. The stream may be null to indicate + * that the data entry should not be written. + */ + public OutputStream getOutputStream(DataEntry dataEntry, + Finisher finisher) throws IOException; + + + /** + * Finishes writing all data entries. + */ + public void close() throws IOException; +} diff --git a/src/proguard/io/DirectoryFilter.java b/src/proguard/io/DirectoryFilter.java new file mode 100644 index 000000000..72e2e6de4 --- /dev/null +++ b/src/proguard/io/DirectoryFilter.java @@ -0,0 +1,58 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; +import proguard.util.ExtensionMatcher; + +import java.io.IOException; + + +/** + * This DataEntryReader delegates to one of two other DataEntryReader instances, + * depending on whether the data entry represents a directory or not. + * + * @author Eric Lafortune + */ +public class DirectoryFilter extends FilteredDataEntryReader +{ + /** + * Creates a new ClassFilter that delegates reading directories to the + * given reader. + */ + public DirectoryFilter(DataEntryReader directoryReader) + { + this (directoryReader, null); + } + + + /** + * Creates a new ClassFilter that delegates to either of the two given + * readers. + */ + public DirectoryFilter(DataEntryReader directoryReader, + DataEntryReader otherReader) + { + super(new DataEntryDirectoryFilter(), + directoryReader, + otherReader); + } +} \ No newline at end of file diff --git a/src/proguard/io/DirectoryPump.java b/src/proguard/io/DirectoryPump.java new file mode 100644 index 000000000..cd6c2ab20 --- /dev/null +++ b/src/proguard/io/DirectoryPump.java @@ -0,0 +1,78 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; + + +/** + * This class can read a given file or directory, recursively, applying a given + * DataEntryReader to all files it comes across. + * + * @author Eric Lafortune + */ +public class DirectoryPump implements DataEntryPump +{ + private final File directory; + + + public DirectoryPump(File directory) + { + this.directory = directory; + } + + + // Implementations for DataEntryPump. + + public void pumpDataEntries(DataEntryReader dataEntryReader) + throws IOException + { + if (!directory.exists()) + { + throw new IOException("No such file or directory"); + } + + readFiles(directory, dataEntryReader); + } + + + /** + * Reads the given subdirectory recursively, applying the given DataEntryReader + * to all files that are encountered. + */ + private void readFiles(File file, DataEntryReader dataEntryReader) + throws IOException + { + // Pass the file data entry to the reader. + dataEntryReader.read(new FileDataEntry(directory, file)); + + if (file.isDirectory()) + { + // Recurse into the subdirectory. + File[] files = file.listFiles(); + + for (int index = 0; index < files.length; index++) + { + readFiles(files[index], dataEntryReader); + } + } + } +} diff --git a/src/proguard/io/DirectoryWriter.java b/src/proguard/io/DirectoryWriter.java new file mode 100644 index 000000000..7948ee225 --- /dev/null +++ b/src/proguard/io/DirectoryWriter.java @@ -0,0 +1,165 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +import java.io.*; + + +/** + * This DataEntryWriter writes data entries to individual files in a given + * directory. + * + * @author Eric Lafortune + */ +public class DirectoryWriter implements DataEntryWriter +{ + private final File baseFile; + private final boolean isFile; + + private File currentFile; + private OutputStream currentOutputStream; + private Finisher currentFinisher; + + + /** + * Creates a new DirectoryWriter. + * @param baseFile the base directory to which all files will be written. + */ + public DirectoryWriter(File baseFile, + boolean isFile) + { + this.baseFile = baseFile; + this.isFile = isFile; + } + + + // Implementations for DataEntryWriter. + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + // Should we close the current file? + if (!isFile && + currentFile != null) + { + closeEntry(); + } + + File directory = getFile(dataEntry); + if (!directory.exists() && + !directory.mkdirs()) + { + throw new IOException("Can't create directory [" + directory.getPath() + "]"); + } + + return true; + } + + + public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + { + return getOutputStream(dataEntry, null); + } + + + public OutputStream getOutputStream(DataEntry dataEntry, + Finisher finisher) throws IOException + { + File file = getFile(dataEntry); + + // Should we close the current file? + if (!isFile && + currentFile != null && + !currentFile.equals(file)) + { + closeEntry(); + } + + // Do we need a new stream? + if (currentOutputStream == null) + { + // Make sure the parent directories exist. + File parentDirectory = file.getParentFile(); + if (parentDirectory != null && + !parentDirectory.exists() && + !parentDirectory.mkdirs()) + { + throw new IOException("Can't create directory [" + parentDirectory.getPath() + "]"); + } + + // Open a new output stream for writing to the file. + currentOutputStream = + new BufferedOutputStream( + new FileOutputStream(file)); + + currentFinisher = finisher; + currentFile = file; + } + + return currentOutputStream; + } + + + public void close() throws IOException + { + // Close the file stream, if any. + closeEntry(); + } + + + // Small utility methods. + + /** + * Returns the file for the given data entry. + */ + private File getFile(DataEntry dataEntry) + { + // Use the specified file, or construct a new file. + return isFile ? + baseFile : + new File(baseFile, + dataEntry.getName().replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + File.separatorChar)); + } + + + /** + * Closes the previous file, if any. + */ + private void closeEntry() throws IOException + { + // Close the file stream, if any. + if (currentOutputStream != null) + { + // Let any finisher finish up first. + if (currentFinisher != null) + { + currentFinisher.finish(); + currentFinisher = null; + } + + currentOutputStream.close(); + currentOutputStream = null; + currentFile = null; + } + } +} diff --git a/src/proguard/io/FileDataEntry.java b/src/proguard/io/FileDataEntry.java new file mode 100644 index 000000000..618a092b0 --- /dev/null +++ b/src/proguard/io/FileDataEntry.java @@ -0,0 +1,96 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +import java.io.*; + +/** + * This DataEntry represents a file. + * + * @author Eric Lafortune + */ +public class FileDataEntry implements DataEntry +{ + private final File directory; + private final File file; + private InputStream inputStream; + + + public FileDataEntry(File directory, + File file) + { + this.directory = directory; + this.file = file; + } + + + // Implementations for DataEntry. + + public String getName() + { + // Chop the directory name from the file name and get the right separators. + return file.equals(directory) ? + file.getName() : + file.getPath() + .substring(directory.getPath().length() + File.separator.length()) + .replace(File.separatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR); + } + + + public boolean isDirectory() + { + return file.isDirectory(); + } + + + public InputStream getInputStream() throws IOException + { + if (inputStream == null) + { + inputStream = new BufferedInputStream(new FileInputStream(file)); + } + + return inputStream; + } + + + public void closeInputStream() throws IOException + { + inputStream.close(); + inputStream = null; + } + + + public DataEntry getParent() + { + return null; + } + + + // Implementations for Object. + + public String toString() + { + return getName(); + } +} diff --git a/src/proguard/io/FilteredDataEntryReader.java b/src/proguard/io/FilteredDataEntryReader.java new file mode 100644 index 000000000..03b5dd81c --- /dev/null +++ b/src/proguard/io/FilteredDataEntryReader.java @@ -0,0 +1,90 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.IOException; + + +/** + * This DataEntryReader delegates to one of two other DataEntryReader instances, + * depending on whether the data entry passes through a given data entry filter + * or not. + * + * @author Eric Lafortune + */ +public class FilteredDataEntryReader implements DataEntryReader +{ + private final DataEntryFilter dataEntryFilter; + private final DataEntryReader acceptedDataEntryReader; + private final DataEntryReader rejectedDataEntryReader; + + + /** + * Creates a new FilteredDataEntryReader with only a reader for accepted + * data entries. + * @param dataEntryFilter the data entry filter. + * @param acceptedDataEntryReader the DataEntryReader to which the reading + * will be delegated if the filter accepts + * the data entry. May be null. + */ + public FilteredDataEntryReader(DataEntryFilter dataEntryFilter, + DataEntryReader acceptedDataEntryReader) + { + this(dataEntryFilter, acceptedDataEntryReader, null); + } + + + /** + * Creates a new FilteredDataEntryReader. + * @param dataEntryFilter the data entry filter. + * @param acceptedDataEntryReader the DataEntryReader to which the reading + * will be delegated if the filter accepts + * the data entry. May be null. + * @param rejectedDataEntryReader the DataEntryReader to which the reading + * will be delegated if the filter does not + * accept the data entry. May be + * null. + */ + public FilteredDataEntryReader(DataEntryFilter dataEntryFilter, + DataEntryReader acceptedDataEntryReader, + DataEntryReader rejectedDataEntryReader) + { + this.dataEntryFilter = dataEntryFilter; + this.acceptedDataEntryReader = acceptedDataEntryReader; + this.rejectedDataEntryReader = rejectedDataEntryReader; + } + + + // Implementations for DataEntryReader. + + public void read(DataEntry dataEntry) + throws IOException + { + DataEntryReader dataEntryReader = dataEntryFilter.accepts(dataEntry) ? + acceptedDataEntryReader : + rejectedDataEntryReader; + + if (dataEntryReader != null) + { + dataEntryReader.read(dataEntry); + } + } +} diff --git a/src/proguard/io/FilteredDataEntryWriter.java b/src/proguard/io/FilteredDataEntryWriter.java new file mode 100644 index 000000000..b3b751ce2 --- /dev/null +++ b/src/proguard/io/FilteredDataEntryWriter.java @@ -0,0 +1,125 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; + +/** + * This DataEntryWriter delegates to one of two other DataEntryWriter instances, + * depending on whether the data entry passes through a given data entry filter + * or not. + * + * @author Eric Lafortune + */ +public class FilteredDataEntryWriter implements DataEntryWriter +{ + private final DataEntryFilter dataEntryFilter; + private DataEntryWriter acceptedDataEntryWriter; + private DataEntryWriter rejectedDataEntryWriter; + + + /** + * Creates a new FilteredDataEntryWriter with only a writer for accepted + * data entries. + * @param dataEntryFilter the data entry filter. + * @param acceptedDataEntryWriter the DataEntryWriter to which the writing + * will be delegated if the filter accepts + * the data entry. May be null. + */ + public FilteredDataEntryWriter(DataEntryFilter dataEntryFilter, + DataEntryWriter acceptedDataEntryWriter) + { + this(dataEntryFilter, acceptedDataEntryWriter, null); + } + + + /** + * Creates a new FilteredDataEntryWriter. + * @param dataEntryFilter the data entry filter. + * @param acceptedDataEntryWriter the DataEntryWriter to which the writing + * will be delegated if the filter accepts + * the data entry. May be null. + * @param rejectedDataEntryWriter the DataEntryWriter to which the writing + * will be delegated if the filter does not + * accept the data entry. May be + * null. + */ + public FilteredDataEntryWriter(DataEntryFilter dataEntryFilter, + DataEntryWriter acceptedDataEntryWriter, + DataEntryWriter rejectedDataEntryWriter) + { + this.dataEntryFilter = dataEntryFilter; + this.acceptedDataEntryWriter = acceptedDataEntryWriter; + this.rejectedDataEntryWriter = rejectedDataEntryWriter; + } + + + // Implementations for DataEntryWriter. + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + // Get the right data entry writer. + DataEntryWriter dataEntryWriter = dataEntryFilter.accepts(dataEntry) ? + acceptedDataEntryWriter : + rejectedDataEntryWriter; + + // Delegate to it, if it's not null. + return dataEntryWriter != null && + dataEntryWriter.createDirectory(dataEntry); + } + + + public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + { + return getOutputStream(dataEntry, null); + } + + + public OutputStream getOutputStream(DataEntry dataEntry, + Finisher finisher) throws IOException + { + // Get the right data entry writer. + DataEntryWriter dataEntryWriter = dataEntryFilter.accepts(dataEntry) ? + acceptedDataEntryWriter : + rejectedDataEntryWriter; + + // Delegate to it, if it's not null. + return dataEntryWriter != null ? + dataEntryWriter.getOutputStream(dataEntry, finisher) : + null; + } + + + public void close() throws IOException + { + if (acceptedDataEntryWriter != null) + { + acceptedDataEntryWriter.close(); + acceptedDataEntryWriter = null; + } + + if (rejectedDataEntryWriter != null) + { + rejectedDataEntryWriter.close(); + rejectedDataEntryWriter = null; + } + } +} diff --git a/src/proguard/io/Finisher.java b/src/proguard/io/Finisher.java new file mode 100644 index 000000000..7d5f93f06 --- /dev/null +++ b/src/proguard/io/Finisher.java @@ -0,0 +1,37 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.IOException; + +/** + * This interface specifies a listener that is called to finish an output stream + * before it is closed. + * + * @author Eric Lafortune + */ +public interface Finisher +{ + /** + * Finishes an output stream right before it is closed. + */ + public void finish() throws IOException; +} diff --git a/src/proguard/io/JarReader.java b/src/proguard/io/JarReader.java new file mode 100644 index 000000000..be4c97b64 --- /dev/null +++ b/src/proguard/io/JarReader.java @@ -0,0 +1,75 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.IOException; +import java.util.zip.*; + +/** + * This DataEntryReader lets a given DataEntryReader read all data entries of + * the read jar/war/zip data entries. + * + * @author Eric Lafortune + */ +public class JarReader implements DataEntryReader +{ + private final DataEntryReader dataEntryReader; + + + /** + * Creates a new JarReader. + */ + public JarReader(DataEntryReader dataEntryReader) + { + this.dataEntryReader = dataEntryReader; + } + + + // Implementation for DataEntryReader. + + public void read(DataEntry dataEntry) throws IOException + { + ZipInputStream zipInputStream = new ZipInputStream(dataEntry.getInputStream()); + + try + { + // Get all entries from the input jar. + while (true) + { + // Can we get another entry? + ZipEntry zipEntry = zipInputStream.getNextEntry(); + if (zipEntry == null) + { + break; + } + + // Delegate the actual reading to the data entry reader. + dataEntryReader.read(new ZipDataEntry(dataEntry, + zipEntry, + zipInputStream)); + } + } + finally + { + dataEntry.closeInputStream(); + } + } +} diff --git a/src/proguard/io/JarWriter.java b/src/proguard/io/JarWriter.java new file mode 100644 index 000000000..d85e63b44 --- /dev/null +++ b/src/proguard/io/JarWriter.java @@ -0,0 +1,235 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.zip.*; + + +/** + * This DataEntryWriter sends data entries to a given jar/zip file. + * The manifest and comment properties can optionally be set. + * + * @author Eric Lafortune + */ +public class JarWriter implements DataEntryWriter, Finisher +{ + private final DataEntryWriter dataEntryWriter; + private final Manifest manifest; + private final String comment; + + private OutputStream currentParentOutputStream; + private ZipOutputStream currentJarOutputStream; + private Finisher currentFinisher; + private DataEntry currentDataEntry; + + // The names of the jar entries that are already in the jar. + private final Set jarEntryNames = new HashSet(); + + + /** + * Creates a new JarWriter without manifest or comment. + */ + public JarWriter(DataEntryWriter dataEntryWriter) + { + this(dataEntryWriter, null, null); + } + + + /** + * Creates a new JarWriter. + */ + public JarWriter(DataEntryWriter dataEntryWriter, + Manifest manifest, + String comment) + { + this.dataEntryWriter = dataEntryWriter; + this.manifest = manifest; + this.comment = comment; + } + + + // Implementations for DataEntryWriter. + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + //Make sure we can start with a new entry. + if (!prepareEntry(dataEntry)) + { + return false; + } + + // Close the previous ZIP entry, if any. + closeEntry(); + + // Get the directory entry name. + String name = dataEntry.getName() + ClassConstants.INTERNAL_PACKAGE_SEPARATOR; + + // We have to check if the name is already used, because + // ZipOutputStream doesn't handle this case properly (it throws + // an exception which can be caught, but the ZipDataEntry is + // remembered anyway). + if (jarEntryNames.add(name)) + { + // Create a new directory entry. + currentJarOutputStream.putNextEntry(new ZipEntry(name)); + currentJarOutputStream.closeEntry(); + } + + // Clear the finisher. + currentFinisher = null; + currentDataEntry = null; + + return true; + } + + + public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + { + return getOutputStream(dataEntry, null); + } + + + public OutputStream getOutputStream(DataEntry dataEntry, + Finisher finisher) throws IOException + { + //Make sure we can start with a new entry. + if (!prepareEntry(dataEntry)) + { + return null; + } + + // Do we need a new entry? + 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). + if (!jarEntryNames.add(name)) + { + throw new IOException("Duplicate zip entry ["+dataEntry+"]"); + } + + // Create a new entry. + currentJarOutputStream.putNextEntry(new ZipEntry(name)); + + // Set up the finisher for the entry. + currentFinisher = finisher; + currentDataEntry = dataEntry; + } + + return currentJarOutputStream; + } + + + public void finish() throws IOException + { + // Finish the entire ZIP stream, if any. + if (currentJarOutputStream != null) + { + // Close the previous ZIP entry, if any. + closeEntry(); + + // Finish the entire ZIP stream. + currentJarOutputStream.finish(); + currentJarOutputStream = null; + currentParentOutputStream = null; + jarEntryNames.clear(); + } + } + + + public void close() throws IOException + { + // Close the parent stream. + dataEntryWriter.close(); + } + + + // Small utility methods. + + /** + * Makes sure the current output stream is set up for the given entry. + */ + private boolean prepareEntry(DataEntry dataEntry) throws IOException + { + // Get the parent stream, new or exisiting. + // This may finish our own jar output stream. + OutputStream parentOutputStream = + dataEntryWriter.getOutputStream(dataEntry.getParent(), this); + + // Did we get a stream? + if (parentOutputStream == null) + { + return false; + } + + // Do we need a new stream? + if (currentParentOutputStream == null) + { + currentParentOutputStream = parentOutputStream; + + // Create a new jar stream, with a manifest, if set. + currentJarOutputStream = manifest != null ? + new JarOutputStream(parentOutputStream, manifest) : + new ZipOutputStream(parentOutputStream); + + // Add a comment, if set. + if (comment != null) + { + currentJarOutputStream.setComment(comment); + } + } + + return true; + } + + + /** + * Closes the previous ZIP entry, if any. + */ + private void closeEntry() throws IOException + { + if (currentDataEntry != null) + { + // Let any finisher finish up first. + if (currentFinisher != null) + { + currentFinisher.finish(); + currentFinisher = null; + } + + currentJarOutputStream.closeEntry(); + currentDataEntry = null; + } + } +} diff --git a/src/proguard/io/ManifestRewriter.java b/src/proguard/io/ManifestRewriter.java new file mode 100644 index 000000000..8a8c7ae82 --- /dev/null +++ b/src/proguard/io/ManifestRewriter.java @@ -0,0 +1,211 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.io; + +import proguard.classfile.*; + +import java.io.*; + +/** + * This DataEntryReader writes the manifest data entries that it reads to a + * given DataEntryWriter, updating their contents based on the renamed classes + * in the given ClassPool. + * + * @author Eric Lafortune + */ +public class ManifestRewriter extends DataEntryRewriter +{ + /** + * Creates a new ManifestRewriter. + */ + public ManifestRewriter(ClassPool classPool, + DataEntryWriter dataEntryWriter) + { + super(classPool, dataEntryWriter); + } + + + // Implementations for DataEntryRewriter. + + protected void copyData(Reader reader, + Writer writer) + throws IOException + { + super.copyData(new SplitLineReader(reader), + new SplitLineWriter(writer)); + } + + + /** + * This Reader reads manifest files, joining any split lines. It replaces + * the allowed CR/LF/CR+LF alternatives by simple LF in the process. + */ + private static class SplitLineReader extends FilterReader + { + private static final int NONE = -2; + + private int bufferedCharacter = NONE; + + + public SplitLineReader(Reader reader) + { + super(reader); + } + + + // Implementations for Reader. + + public int read() throws IOException + { + while (true) + { + // Get the buffered character or the first character. + int c1 = bufferedCharacter != NONE ? + bufferedCharacter : + super.read(); + + // Clear the buffered character. + bufferedCharacter = NONE; + + // Return it if it's an ordinary character. + if (c1 != '\n' && c1 != '\r') + { + return c1; + } + + // It's a newline. Read the second character to see if it's a + // continuation. + int c2 = super.read(); + + // Skip any corresponding, redundant \n or \r. + if ((c2 == '\n' || c2 == '\r') && c1 != c2) + { + c2 = super.read(); + } + + // Isn't it a continuation after all? + if (c2 != ' ') + { + // Buffer the second character and return a newline. + bufferedCharacter = c2; + return '\n'; + } + + // Just continue after the continuation characters. + } + } + + + public int read(char[] cbuf, int off, int len) throws IOException + { + // Delegate to reading a single character at a time. + int count = 0; + while (count < len) + { + int c = read(); + if (c == -1) + { + break; + } + + cbuf[off + count++] = (char)c; + } + + return count; + } + + + public long skip(long n) throws IOException + { + // Delegate to reading a single character at a time. + int count = 0; + while (count < n) + { + int c = read(); + if (c == -1) + { + break; + } + + count++; + } + + return count; + } + } + + + /** + * This Writer writes manifest files, splitting any long lines. + */ + private static class SplitLineWriter extends FilterWriter + { + private int counter = 0; + + + public SplitLineWriter(Writer writer) + { + super(writer); + } + + + // Implementations for Reader. + + public void write(int c) throws IOException + { + // TODO: We should actually count the Utf-8 bytes, not the characters. + if (c == '\n') + { + // Reset the character count. + counter = 0; + } + else if (counter == 70) + { + // Insert a newline and a space. + super.write('\n'); + super.write(' '); + + counter = 2; + } + else + { + counter++; + } + + super.write(c); + } + + + public void write(char[] cbuf, int off, int len) throws IOException + { + for (int count = 0; count < len; count++) + { + write(cbuf[off + count]); + } + } + + + public void write(String str, int off, int len) throws IOException + { + write(str.toCharArray(), off, len); + } + } +} \ No newline at end of file diff --git a/src/proguard/io/NameFilter.java b/src/proguard/io/NameFilter.java new file mode 100644 index 000000000..67d630ef4 --- /dev/null +++ b/src/proguard/io/NameFilter.java @@ -0,0 +1,83 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.io; + +import proguard.util.*; + +import java.util.List; + +/** + * This DataEntryReader delegates to one of two other DataEntryReader instances, + * depending on the name of the data entry. + * + * @author Eric Lafortune + */ +public class NameFilter extends FilteredDataEntryReader +{ + /** + * Creates a new NameFilter that delegates to the given reader, depending + * on the given list of filters. + */ + public NameFilter(String regularExpression, + DataEntryReader acceptedDataEntryReader) + { + this(regularExpression, acceptedDataEntryReader, null); + } + + + /** + * Creates a new NameFilter that delegates to either of the two given + * readers, depending on the given list of filters. + */ + public NameFilter(String regularExpression, + DataEntryReader acceptedDataEntryReader, + DataEntryReader rejectedDataEntryReader) + { + super(new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(regularExpression)), + acceptedDataEntryReader, + rejectedDataEntryReader); + } + + + /** + * Creates a new NameFilter that delegates to the given reader, depending + * on the given list of filters. + */ + public NameFilter(List regularExpressions, + DataEntryReader acceptedDataEntryReader) + { + this(regularExpressions, acceptedDataEntryReader, null); + } + + + /** + * Creates a new NameFilter that delegates to either of the two given + * readers, depending on the given list of filters. + */ + public NameFilter(List regularExpressions, + DataEntryReader acceptedDataEntryReader, + DataEntryReader rejectedDataEntryReader) + { + super(new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(regularExpressions)), + acceptedDataEntryReader, + rejectedDataEntryReader); + } +} \ No newline at end of file diff --git a/src/proguard/io/ParentDataEntryWriter.java b/src/proguard/io/ParentDataEntryWriter.java new file mode 100644 index 000000000..f24ef37fe --- /dev/null +++ b/src/proguard/io/ParentDataEntryWriter.java @@ -0,0 +1,75 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; + +/** + * This DataEntryWriter lets another DataEntryWriter write the parent data + * entries. + * + * @author Eric Lafortune + */ +public class ParentDataEntryWriter implements DataEntryWriter +{ + private DataEntryWriter dataEntryWriter; + + + /** + * Creates a new ParentDataEntryWriter. + * @param dataEntryWriter the DataEntryWriter to which the writing will be + * delegated, passing the data entries' parents. + */ + public ParentDataEntryWriter(DataEntryWriter dataEntryWriter) + { + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryWriter. + + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + return getOutputStream(dataEntry) != null; + } + + + public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + { + return getOutputStream(dataEntry, null); + } + + + public OutputStream getOutputStream(DataEntry dataEntry, + Finisher finisher) throws IOException + { + return dataEntryWriter.getOutputStream(dataEntry.getParent(), + finisher); + } + + + public void close() throws IOException + { + dataEntryWriter.close(); + dataEntryWriter = null; + } +} diff --git a/src/proguard/io/RenamedDataEntry.java b/src/proguard/io/RenamedDataEntry.java new file mode 100644 index 000000000..a0f5657c7 --- /dev/null +++ b/src/proguard/io/RenamedDataEntry.java @@ -0,0 +1,83 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; + +/** + * This DataEntry wraps another data entry, returning a different name instead + * of the wrapped data entry's name. + * + * @author Eric Lafortune + */ +public class RenamedDataEntry implements DataEntry +{ + private final DataEntry dataEntry; + private final String name; + + + public RenamedDataEntry(DataEntry dataEntry, + String name) + { + this.dataEntry = dataEntry; + this.name = name; + } + + + // Implementations for DataEntry. + + public String getName() + { + return name; + } + + + public boolean isDirectory() + { + return dataEntry.isDirectory(); + } + + + public InputStream getInputStream() throws IOException + { + return dataEntry.getInputStream(); + } + + + public void closeInputStream() throws IOException + { + dataEntry.closeInputStream(); + } + + + public DataEntry getParent() + { + return dataEntry.getParent(); + } + + + // Implementations for Object. + + public String toString() + { + return name + " == " + dataEntry; + } +} diff --git a/src/proguard/io/ZipDataEntry.java b/src/proguard/io/ZipDataEntry.java new file mode 100644 index 000000000..20a9d3bbc --- /dev/null +++ b/src/proguard/io/ZipDataEntry.java @@ -0,0 +1,98 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstants; + +import java.io.*; +import java.util.zip.*; + +/** + * This DataEntry represents a ZIP entry. + * + * @author Eric Lafortune + */ +public class ZipDataEntry implements DataEntry +{ + private final DataEntry parent; + private final ZipEntry zipEntry; + private ZipInputStream zipInputStream; + + + public ZipDataEntry(DataEntry parent, + ZipEntry zipEntry, + ZipInputStream zipInputStream) + { + this.parent = parent; + this.zipEntry = zipEntry; + this.zipInputStream = zipInputStream; + } + + + // Implementations for DataEntry. + + public String getName() + { + // Get the right separators. + String name = zipEntry.getName() + .replace(File.separatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR); + + // Chop the trailing directory slash, if any. + int length = name.length(); + return length > 0 && + name.charAt(length-1) == ClassConstants.INTERNAL_PACKAGE_SEPARATOR ? + name.substring(0, length -1) : + name; + } + + + public boolean isDirectory() + { + return zipEntry.isDirectory(); + } + + + public InputStream getInputStream() throws IOException + { + return zipInputStream; + } + + + public void closeInputStream() throws IOException + { + zipInputStream.closeEntry(); + zipInputStream = null; + } + + + public DataEntry getParent() + { + return parent; + } + + + // Implementations for Object. + + public String toString() + { + return parent.toString() + ':' + getName(); + } +} diff --git a/src/proguard/io/package.html b/src/proguard/io/package.html new file mode 100644 index 000000000..4ad9f4193 --- /dev/null +++ b/src/proguard/io/package.html @@ -0,0 +1,4 @@ + +This package contains classes to read and write files, optionally wrapped in +jars, wars, ears, zips, directories,... + diff --git a/src/proguard/obfuscate/AttributeShrinker.java b/src/proguard/obfuscate/AttributeShrinker.java new file mode 100644 index 000000000..0c3ab944c --- /dev/null +++ b/src/proguard/obfuscate/AttributeShrinker.java @@ -0,0 +1,120 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +import java.util.Arrays; + +/** + * This ClassVisitor removes attributes that are not marked as being used or + * required. + * + * @see AttributeUsageMarker + * + * @author Eric Lafortune + */ +public class AttributeShrinker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Compact the array for class attributes. + programClass.u2attributesCount = + shrinkArray(programClass.attributes, + programClass.u2attributesCount); + + // Compact the attributes in fields, methods, and class attributes, + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Library classes are left unchanged. + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Compact the attributes array. + programMember.u2attributesCount = + shrinkArray(programMember.attributes, + programMember.u2attributesCount); + + // Compact any attributes of the remaining attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Compact the attributes array. + codeAttribute.u2attributesCount = + shrinkArray(codeAttribute.attributes, + codeAttribute.u2attributesCount); + } + + + // Small utility methods. + + /** + * Removes all VisitorAccepter objects that are not marked as being used + * from the given array. + * @return the new number of VisitorAccepter objects. + */ + private static int shrinkArray(VisitorAccepter[] array, int length) + { + int counter = 0; + + // Shift the used objects together. + for (int index = 0; index < length; index++) + { + if (AttributeUsageMarker.isUsed(array[index])) + { + array[counter++] = array[index]; + } + } + + // Clear the remaining array elements. + Arrays.fill(array, counter, length, null); + + return counter; + } +} diff --git a/src/proguard/obfuscate/AttributeUsageMarker.java b/src/proguard/obfuscate/AttributeUsageMarker.java new file mode 100644 index 000000000..32a512b8c --- /dev/null +++ b/src/proguard/obfuscate/AttributeUsageMarker.java @@ -0,0 +1,71 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.Attribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor marks all attributes that it visits. + * + * @see AttributeShrinker + * + * @author Eric Lafortune + */ +public class AttributeUsageMarker +extends SimplifiedVisitor +implements AttributeVisitor +{ + // A visitor info flag to indicate the attribute is being used. + private static final Object USED = new Object(); + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + markAsUsed(attribute); + } + + + // Small utility methods. + + /** + * Marks the given VisitorAccepter as being used (or useful). + * In this context, the VisitorAccepter will be an Attribute object. + */ + private static void markAsUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(USED); + } + + + /** + * Returns whether the given VisitorAccepter has been marked as being used. + * In this context, the VisitorAccepter will be an Attribute object. + */ + static boolean isUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == USED; + } +} diff --git a/src/proguard/obfuscate/ClassObfuscator.java b/src/proguard/obfuscate/ClassObfuscator.java new file mode 100644 index 000000000..9e1a91c79 --- /dev/null +++ b/src/proguard/obfuscate/ClassObfuscator.java @@ -0,0 +1,541 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 proguard.util.*; + +import java.util.*; + +/** + * This ClassVisitor 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 DictionaryNameFactory classNameFactory; + private final DictionaryNameFactory packageNameFactory; + private final boolean useMixedCaseClassNames; + private final StringMatcher keepPackageNamesMatcher; + private final String flattenPackageHierarchy; + private final String repackageClasses; + private final boolean allowAccessModification; + + private final Set classNamesToAvoid = new HashSet(); + + // 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 - numeric class name factory] + private final Map packagePrefixClassNameFactoryMap = new HashMap(); + + // Map: [package prefix - numeric class name factory] + private final Map packagePrefixNumericClassNameFactoryMap = new HashMap(); + + // Field acting as temporary variables and as return values for names + // of outer classes and types of inner classes. + private String newClassName; + private boolean numericClassName; + + + /** + * Creates a new ClassObfuscator. + * @param programClassPool the class pool in which class names + * have to be unique. + * @param classNameFactory the optional class obfuscation dictionary. + * @param packageNameFactory the optional package obfuscation + * dictionary. + * @param useMixedCaseClassNames specifies whether obfuscated packages and + * classes can get mixed-case names. + * @param keepPackageNames the optional filter for which matching + * package names are kept. + * @param flattenPackageHierarchy the base package if the obfuscated package + * hierarchy is to be flattened. + * @param repackageClasses the base package if the obfuscated classes + * are to be repackaged. + * @param allowAccessModification specifies whether obfuscated classes can + * be freely moved between packages. + */ + public ClassObfuscator(ClassPool programClassPool, + DictionaryNameFactory classNameFactory, + DictionaryNameFactory packageNameFactory, + boolean useMixedCaseClassNames, + List keepPackageNames, + String flattenPackageHierarchy, + String repackageClasses, + boolean allowAccessModification) + { + this.classNameFactory = classNameFactory; + this.packageNameFactory = packageNameFactory; + + // 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.keepPackageNamesMatcher = keepPackageNames == null ? null : + new ListParser(new FileNameParser()).parse(keepPackageNames); + 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 an outer class prefix, if any, or it may be the fixed base + // package, if classes are to be repackaged. + String newPackagePrefix = newClassName != null ? + newClassName + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR : + newPackagePrefix(ClassUtil.internalPackagePrefix(programClass.getName())); + + // Come up with a new class name, numeric or ordinary. + newClassName = newClassName != null && numericClassName ? + generateUniqueNumericClassName(newPackagePrefix) : + generateUniqueClassName(newPackagePrefix); + + setNewClassName(programClass, newClassName); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // This can happen for dubious input, if the outer class of a program + // class is a library class, and its name is requested. + newClassName = libraryClass.getName(); + } + + + // 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); + + String innerClassName = clazz.getName(); + String outerClassName = clazz.getClassName(enclosingMethodAttribute.u2classIndex); + + numericClassName = isNumericClassName(innerClassName, + outerClassName); + } + + + // 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) + { + String innerClassName = clazz.getClassName(innerClassIndex); + if (innerClassName.equals(clazz.getName())) + { + clazz.constantPoolEntryAccept(outerClassIndex, this); + + String outerClassName = clazz.getClassName(outerClassIndex); + + numericClassName = isNumericClassName(innerClassName, + outerClassName); + } + } + } + + + /** + * Returns whether the given inner class name is a numeric name. + */ + private boolean isNumericClassName(String innerClassName, + String outerClassName) + { + int innerClassNameStart = outerClassName.length() + 1; + int innerClassNameLength = innerClassName.length(); + + if (innerClassNameStart >= innerClassNameLength) + { + return false; + } + + for (int index = innerClassNameStart; index < innerClassNameLength; index++) + { + if (!Character.isDigit(innerClassName.charAt(index))) + { + return false; + } + } + + return true; + } + + + // 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 recursively 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 keeping the package name? + if (keepPackageNamesMatcher != null && + keepPackageNamesMatcher.matches(packagePrefix.length() > 0 ? + packagePrefix.substring(0, packagePrefix.length()-1) : + packagePrefix)) + { + return packagePrefix; + } + + // Are we forcing a new package prefix? + if (repackageClasses != null) + { + return repackageClasses; + } + + // Are we forcing a new superpackage prefix? + // Otherwise 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); + if (this.packageNameFactory != null) + { + packageNameFactory = + new DictionaryNameFactory(this.packageNameFactory, + packageNameFactory); + } + + packagePrefixPackageNameFactoryMap.put(newSuperPackagePrefix, + packageNameFactory); + } + + return generateUniquePackagePrefix(newSuperPackagePrefix, packageNameFactory); + } + + + /** + * Creates a new package prefix in the given new superpackage, with the + * given package name factory. + */ + private String generateUniquePackagePrefix(String newSuperPackagePrefix, + NameFactory packageNameFactory) + { + // Come up with package names until we get an original one. + String newPackagePrefix; + do + { + // 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); + if (this.classNameFactory != null) + { + classNameFactory = + new DictionaryNameFactory(this.classNameFactory, + classNameFactory); + } + + packagePrefixClassNameFactoryMap.put(newPackagePrefix, + classNameFactory); + } + + return generateUniqueClassName(newPackagePrefix, classNameFactory); + } + + + /** + * Creates a new class name in the given new package. + */ + private String generateUniqueNumericClassName(String newPackagePrefix) + { + // Find the right name factory for this package. + NameFactory classNameFactory = + (NameFactory)packagePrefixNumericClassNameFactoryMap.get(newPackagePrefix); + if (classNameFactory == null) + { + // We haven't seen classes in this package before. + // Create a new name factory for them. + classNameFactory = new NumericNameFactory(); + + packagePrefixNumericClassNameFactoryMap.put(newPackagePrefix, + classNameFactory); + } + + return generateUniqueClassName(newPackagePrefix, classNameFactory); + } + + + /** + * Creates a new class name in the given new package, with the given + * class name factory. + */ + private String generateUniqueClassName(String newPackagePrefix, + NameFactory classNameFactory) + { + // Come up with class names until we get an original one. + String newClassName; + String newMixedCaseClassName; + do + { + // Let the factory produce a class name. + newClassName = newPackagePrefix + + classNameFactory.nextName(); + + newMixedCaseClassName = mixedCaseClassName(newClassName); + } + while (classNamesToAvoid.contains(newMixedCaseClassName)); + + // Explicitly make sure the name isn't used again if we have a + // user-specified dictionary and we're not allowed to have mixed case + // class names -- just to protect against problematic dictionaries. + if (this.classNameFactory != null && + !useMixedCaseClassNames) + { + classNamesToAvoid.add(newMixedCaseClassName); + } + + 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 null 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/ClassRenamer.java b/src/proguard/obfuscate/ClassRenamer.java new file mode 100644 index 000000000..4c5e4963f --- /dev/null +++ b/src/proguard/obfuscate/ClassRenamer.java @@ -0,0 +1,109 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.ConstantPoolEditor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor renames the class names and class member + * names of the classes it visits, using names previously determined by the + * obfuscator. + * + * @see ClassObfuscator + * @see MemberObfuscator + * + * @author Eric Lafortune + */ +public class ClassRenamer +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + ConstantVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Rename this class. + programClass.thisClassConstantAccept(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 = + new ConstantPoolEditor(programClass).addUtf8Constant(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 = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newName); + } + } +} diff --git a/src/proguard/obfuscate/DictionaryNameFactory.java b/src/proguard/obfuscate/DictionaryNameFactory.java new file mode 100644 index 000000000..4a7e28c04 --- /dev/null +++ b/src/proguard/obfuscate/DictionaryNameFactory.java @@ -0,0 +1,189 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; +import java.util.*; + +/** + * This NameFactory generates names that are read from a + * specified input file. + * Comments (everything starting with '#' on a single line) are ignored. + * + * @author Eric Lafortune + */ +public class DictionaryNameFactory implements NameFactory +{ + private static final char COMMENT_CHARACTER = '#'; + + + private final List names; + private final NameFactory nameFactory; + + private int index = 0; + + + /** + * Creates a new DictionaryNameFactory. + * @param file the file from which the names can be read. + * @param nameFactory the name factory from which names will be retrieved + * if the list of read names has been exhausted. + */ + public DictionaryNameFactory(File file, + NameFactory nameFactory) throws IOException + { + this.names = new ArrayList(); + this.nameFactory = nameFactory; + + Reader reader = new FileReader(file); + + try + { + StringBuffer buffer = new StringBuffer(); + + while (true) + { + // Read the next character. + int c = reader.read(); + + // Is it a valid identifier character? + if (c != -1 && + (buffer.length() == 0 ? + Character.isJavaIdentifierStart((char)c) : + Character.isJavaIdentifierPart((char)c))) + { + // Append it to the current identifier. + buffer.append((char)c); + } + else + { + // Did we collect a new identifier? + if (buffer.length() > 0) + { + // Add the completed name to the list of names, if it's + // not in it yet. + String name = buffer.toString(); + if (!names.contains(name)) + { + names.add(name); + } + + // Clear the buffer. + buffer.setLength(0); + } + + // Is this the beginning of a comment line? + if (c == COMMENT_CHARACTER) + { + // Skip all characters till the end of the line. + do + { + c = reader.read(); + } + while (c != -1 && + c != '\n' && + c != '\r'); + } + + // Is this the end of the file? + if (c == -1) + { + // Just return. + return; + } + } + } + } + finally + { + reader.close(); + } + } + + + /** + * Creates a new DictionaryNameFactory. + * @param dictionaryNameFactory the dictionary name factory whose dictionary + * will be used. + * @param nameFactory the name factory from which names will be + * retrieved if the list of read names has been + * exhausted. + */ + public DictionaryNameFactory(DictionaryNameFactory dictionaryNameFactory, + NameFactory nameFactory) + { + this.names = dictionaryNameFactory.names; + this.nameFactory = nameFactory; + } + + + // Implementations for NameFactory. + + public void reset() + { + index = 0; + + nameFactory.reset(); + } + + + public String nextName() + { + String name; + + // Do we still have names? + if (index < names.size()) + { + // Return the next name. + name = (String)names.get(index++); + } + else + { + // Return the next different name from the other name factory. + do + { + name = nameFactory.nextName(); + } + while (names.contains(name)); + } + + return name; + } + + + public static void main(String[] args) + { + try + { + DictionaryNameFactory factory = + new DictionaryNameFactory(new File(args[0]), new SimpleNameFactory()); + + for (int counter = 0; counter < 50; counter++) + { + System.out.println("["+factory.nextName()+"]"); + } + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/obfuscate/MapCleaner.java b/src/proguard/obfuscate/MapCleaner.java new file mode 100644 index 000000000..d11f4436c --- /dev/null +++ b/src/proguard/obfuscate/MapCleaner.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.Map; + +/** + * This ClassVisitor clears a given map whenever it visits a class. + * + * @author Eric Lafortune + */ +public class MapCleaner +extends SimplifiedVisitor +implements ClassVisitor +{ + private final Map map; + + + /** + * Creates a new MapCleaner. + * @param map the map to be cleared. + */ + public MapCleaner(Map map) + { + this.map = map; + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + map.clear(); + } +} diff --git a/src/proguard/obfuscate/MappingKeeper.java b/src/proguard/obfuscate/MappingKeeper.java new file mode 100644 index 000000000..7ab1e2577 --- /dev/null +++ b/src/proguard/obfuscate/MappingKeeper.java @@ -0,0 +1,177 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.util.ListUtil; + + +/** + * This MappingKeeper applies the mappings that it receives to its class pool, + * so these mappings are ensured in a subsequent obfuscation step. + * + * @author Eric Lafortune + */ +public class MappingKeeper implements MappingProcessor +{ + private final ClassPool classPool; + private final WarningPrinter warningPrinter; + + // A field acting as a parameter. + private Clazz clazz; + + + /** + * Creates a new MappingKeeper. + * @param classPool the class pool in which class names and class + * member names have to be mapped. + * @param warningPrinter the optional warning printer to which warnings + * can be printed. + */ + public MappingKeeper(ClassPool classPool, + WarningPrinter warningPrinter) + { + this.classPool = classPool; + this.warningPrinter = warningPrinter; + } + + + // Implementations for MappingProcessor. + + public boolean processClassMapping(String className, + String newClassName) + { + // Find the class. + String name = ClassUtil.internalClassName(className); + + clazz = classPool.getClass(name); + if (clazz != null) + { + String newName = ClassUtil.internalClassName(newClassName); + + // Print out a warning if the mapping conflicts with a name that + // was set before. + if (warningPrinter != null) + { + String currentNewName = ClassObfuscator.newClassName(clazz); + if (currentNewName != null && + !currentNewName.equals(newName)) + { + warningPrinter.print(name, + currentNewName, + "Warning: " + + className + + " is not being kept as '" + + ClassUtil.externalClassName(currentNewName) + + "', but remapped to '" + + newClassName + "'"); + } + } + + ClassObfuscator.setNewClassName(clazz, newName); + + // The class members have to be kept as well. + return true; + } + + return false; + } + + + public void processFieldMapping(String className, + String fieldType, + String fieldName, + String newFieldName) + { + if (clazz != null) + { + // Find the field. + String name = fieldName; + String descriptor = ClassUtil.internalType(fieldType); + + 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 = MemberObfuscator.newMemberName(field); + if (currentNewName != null && + !currentNewName.equals(newFieldName)) + { + warningPrinter.print(ClassUtil.internalClassName(className), + "Warning: " + + className + + ": field '" + fieldType + " " + fieldName + + "' is not being kept as '" + currentNewName + + "', but remapped to '" + newFieldName + "'"); + } + } + + // Make sure the mapping name will be kept. + MemberObfuscator.setFixedNewMemberName(field, newFieldName); + } + } + } + + + public void processMethodMapping(String className, + int firstLineNumber, + int lastLineNumber, + String methodReturnType, + String methodName, + String methodArguments, + String newMethodName) + { + if (clazz != null) + { + // Find the method. + String descriptor = ClassUtil.internalMethodDescriptor(methodReturnType, + ListUtil.commaSeparatedList(methodArguments)); + + Method method = clazz.findMethod(methodName, descriptor); + if (method != null) + { + // Print out a warning if the mapping conflicts with a name that + // was set before. + if (warningPrinter != null) + { + String currentNewName = MemberObfuscator.newMemberName(method); + if (currentNewName != null && + !currentNewName.equals(newMethodName)) + { + warningPrinter.print(ClassUtil.internalClassName(className), + "Warning: " + + className + + ": method '" + methodReturnType + " " + methodName + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN + methodArguments + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE + + "' is not being kept as '" + currentNewName + + "', but remapped to '" + newMethodName + "'"); + } + } + + // Make sure the mapping name will be kept. + MemberObfuscator.setFixedNewMemberName(method, newMethodName); + } + } + } +} diff --git a/src/proguard/obfuscate/MappingPrinter.java b/src/proguard/obfuscate/MappingPrinter.java new file mode 100644 index 000000000..a28d10c16 --- /dev/null +++ b/src/proguard/obfuscate/MappingPrinter.java @@ -0,0 +1,147 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.AttributeVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +import java.io.PrintStream; + + +/** + * This ClassVisitor prints out the renamed classes and class members with + * their old names and new names. + * + * @see ClassRenamer + * + * @author Eric Lafortune + */ +public class MappingPrinter +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor +{ + private final PrintStream ps; + + + /** + * Creates a new MappingPrinter that prints to System.out. + */ + public MappingPrinter() + { + this(System.out); + } + + + /** + * Creates a new MappingPrinter that prints to the given stream. + * @param printStream the stream to which to print + */ + public MappingPrinter(PrintStream printStream) + { + this.ps = printStream; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + String name = programClass.getName(); + String newName = ClassObfuscator.newClassName(programClass); + + ps.println(ClassUtil.externalClassName(name) + + " -> " + + ClassUtil.externalClassName(newName) + + ":"); + + // Print out the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + String newName = MemberObfuscator.newMemberName(programField); + if (newName != null) + { + ps.println(" " + + ClassUtil.externalFullFieldDescription( + 0, + programField.getName(programClass), + programField.getDescriptor(programClass)) + + " -> " + + newName); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Special cases: and 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; + } + + String newName = MemberObfuscator.newMemberName(programMethod); + if (newName != null) + { + ps.print(" "); + programMethod.attributesAccept(programClass, this); + ps.println(ClassUtil.externalFullMethodDescription( + programClass.getName(), + 0, + programMethod.getName(programClass), + programMethod.getDescriptor(programClass)) + + " -> " + + newName); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + ps.print(lineNumberTableAttribute.getLowestLineNumber() + ":" + + lineNumberTableAttribute.getHighestLineNumber() + ":"); + } +} diff --git a/src/proguard/obfuscate/MappingProcessor.java b/src/proguard/obfuscate/MappingProcessor.java new file mode 100644 index 000000000..92a916ab8 --- /dev/null +++ b/src/proguard/obfuscate/MappingProcessor.java @@ -0,0 +1,79 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + +/** + * This interface specifies methods to process name mappings between original + * classes and their obfuscated versions. The mappings are typically read + * from a mapping file. + * + * @see MappingReader + * + * @author Eric Lafortune + */ +public interface MappingProcessor +{ + /** + * Processes the given class name mapping. + * + * @param className the original class name. + * @param newClassName the new class name. + * @return whether the processor is interested in receiving mappings of the + * class members of this class. + */ + public boolean processClassMapping(String className, + String newClassName); + + /** + * Processes the given field name mapping. + * + * @param className the original class name. + * @param fieldType the original external field type. + * @param fieldName the original field name. + * @param newFieldName the new field name. + */ + public void processFieldMapping(String className, + String fieldType, + String fieldName, + String newFieldName); + + /** + * Processes the given method name mapping. + * + * @param className the original class name. + * @param firstLineNumber the first line number of the method, or 0 if it + * is not known. + * @param lastLineNumber the last line number of the method, or 0 if it + * is not known. + * @param methodReturnType the original external method return type. + * @param methodName the original external method name. + * @param methodArguments the original external method arguments. + * @param newMethodName the new method name. + */ + public void processMethodMapping(String className, + int firstLineNumber, + int lastLineNumber, + String methodReturnType, + String methodName, + String methodArguments, + String newMethodName); +} diff --git a/src/proguard/obfuscate/MappingReader.java b/src/proguard/obfuscate/MappingReader.java new file mode 100644 index 000000000..51d14ace0 --- /dev/null +++ b/src/proguard/obfuscate/MappingReader.java @@ -0,0 +1,199 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.io.*; + + +/** + * This class can parse mapping files and invoke a processor for each of the + * mapping entries. + * + * @author Eric Lafortune + */ +public class MappingReader +{ + private final File mappingFile; + + + public MappingReader(File mappingFile) + { + this.mappingFile = mappingFile; + } + + + /** + * Reads the mapping file, presenting all of the encountered mapping entries + * to the given processor. + */ + public void pump(MappingProcessor mappingProcessor) throws IOException + { + LineNumberReader reader = new LineNumberReader( + new BufferedReader( + new FileReader(mappingFile))); + try + { + String className = null; + + // Read the subsequent class mappings and class member mappings. + while (true) + { + String line = reader.readLine(); + + if (line == null) + { + break; + } + + line = line.trim(); + + // The distinction between a class mapping and a class + // member mapping is the initial whitespace. + if (line.endsWith(":")) + { + // Process the class mapping and remember the class's + // old name. + className = processClassMapping(line, mappingProcessor); + } + else if (className != null) + { + // Process the class member mapping, in the context of the + // current old class name. + processClassMemberMapping(className, line, mappingProcessor); + } + } + } + catch (IOException ex) + { + throw new IOException("Can't process mapping file (" + ex.getMessage() + ")"); + } + finally + { + try + { + reader.close(); + } + catch (IOException ex) + { + // This shouldn't happen. + } + } + } + + + /** + * 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 processClassMapping(String line, + MappingProcessor mappingProcessor) + { + // See if we can parse "___ -> ___:", containing the original + // class name and the new class name. + + int arrowIndex = line.indexOf("->"); + if (arrowIndex < 0) + { + return null; + } + + int colonIndex = line.indexOf(':', arrowIndex + 2); + if (colonIndex < 0) + { + return null; + } + + // Extract the elements. + String className = line.substring(0, arrowIndex).trim(); + String newClassName = line.substring(arrowIndex + 2, colonIndex).trim(); + + // Process this class name mapping. + boolean interested = mappingProcessor.processClassMapping(className, newClassName); + + return interested ? className : null; + } + + + /** + * Parses the given line with a class member mapping and processes the + * results with the given mapping processor. + */ + private void processClassMemberMapping(String className, + String line, + MappingProcessor mappingProcessor) + { + // See if we can parse "___:___:___ ___(___) -> ___", + // containing the optional line numbers, the return type, the original + // field/method name, optional arguments, and the new field/method name. + + int colonIndex1 = line.indexOf(':'); + int colonIndex2 = colonIndex1 < 0 ? -1 : line.indexOf(':', colonIndex1 + 1); + int spaceIndex = line.indexOf(' ', colonIndex2 + 2); + int argumentIndex1 = line.indexOf('(', spaceIndex + 1); + int argumentIndex2 = argumentIndex1 < 0 ? -1 : line.indexOf(')', argumentIndex1 + 1); + int arrowIndex = line.indexOf("->", Math.max(spaceIndex, argumentIndex2) + 1); + + if (spaceIndex < 0 || + arrowIndex < 0) + { + return; + } + + // Extract the elements. + String type = line.substring(colonIndex2 + 1, spaceIndex).trim(); + String name = line.substring(spaceIndex + 1, argumentIndex1 >= 0 ? argumentIndex1 : arrowIndex).trim(); + String newName = line.substring(arrowIndex + 2).trim(); + + // Process this class member mapping. + if (type.length() > 0 && + name.length() > 0 && + newName.length() > 0) + { + // Is it a field or a method? + if (argumentIndex2 < 0) + { + mappingProcessor.processFieldMapping(className, type, name, newName); + } + else + { + int firstLineNumber = 0; + int lastLineNumber = 0; + + if (colonIndex2 > 0) + { + firstLineNumber = Integer.parseInt(line.substring(0, colonIndex1).trim()); + lastLineNumber = Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim()); + } + + String arguments = line.substring(argumentIndex1 + 1, argumentIndex2).trim(); + + mappingProcessor.processMethodMapping(className, + firstLineNumber, + lastLineNumber, + type, + name, + arguments, + newName); + } + } + } +} diff --git a/src/proguard/obfuscate/MemberNameCleaner.java b/src/proguard/obfuscate/MemberNameCleaner.java new file mode 100644 index 000000000..5205fee49 --- /dev/null +++ b/src/proguard/obfuscate/MemberNameCleaner.java @@ -0,0 +1,60 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 MemberVisitor 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/MemberNameCollector.java b/src/proguard/obfuscate/MemberNameCollector.java new file mode 100644 index 000000000..1544901be --- /dev/null +++ b/src/proguard/obfuscate/MemberNameCollector.java @@ -0,0 +1,106 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; +import proguard.classfile.visitor.MemberVisitor; + +import java.util.Map; + +/** + * This MemberVisitor collects all new (obfuscation) names of the members + * that it visits. + * + * @see MemberObfuscator + * + * @author Eric Lafortune + */ +public class MemberNameCollector +extends SimplifiedVisitor +implements MemberVisitor +{ + private final boolean allowAggressiveOverloading; + private final Map descriptorMap; + + + /** + * 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 MemberNameCollector(boolean allowAggressiveOverloading, + Map descriptorMap) + { + this.allowAggressiveOverloading = allowAggressiveOverloading; + this.descriptorMap = descriptorMap; + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + // Special cases: and are always kept unchanged. + // We can ignore them here. + String name = member.getName(clazz); + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + return; + } + + // Get the member's new name. + String newName = MemberObfuscator.newMemberName(member); + + // Remember it, if it has already been set. + if (newName != null) + { + // Get the member's descriptor. + String descriptor = member.getDescriptor(clazz); + + // Check whether we're allowed to do aggressive overloading + if (!allowAggressiveOverloading) + { + // Trim the return argument from the descriptor if not. + // Works for fields and methods alike. + descriptor = descriptor.substring(0, descriptor.indexOf(')')+1); + } + + // Put the [descriptor - new name] in the map, + // creating a new [new name - old name] map if necessary. + Map nameMap = MemberObfuscator.retrieveNameMap(descriptorMap, descriptor); + + // Isn't there another original name for this new name, or should + // this original name get priority? + 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. + nameMap.put(newName, name); + } + } + } +} diff --git a/src/proguard/obfuscate/MemberNameConflictFixer.java b/src/proguard/obfuscate/MemberNameConflictFixer.java new file mode 100644 index 000000000..68e7c056d --- /dev/null +++ b/src/proguard/obfuscate/MemberNameConflictFixer.java @@ -0,0 +1,159 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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: and 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(clazz.getName(), + "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/MemberNameFilter.java b/src/proguard/obfuscate/MemberNameFilter.java new file mode 100644 index 000000000..6d95270c3 --- /dev/null +++ b/src/proguard/obfuscate/MemberNameFilter.java @@ -0,0 +1,120 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 MemberVisitor delegates its visits to another given + * MemberVisitor, but only when the visited member has a new name. + * Constructors are judged based on the class name. + * + * @see ClassObfuscator + * @see MemberObfuscator + * + * @author Eric Lafortune + */ +public class MemberNameFilter implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MemberNameFilter. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. + */ + public MemberNameFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (hasName(programField)) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (hasName(programClass, programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (hasName(libraryField)) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (hasName(libraryClass, libraryMethod)) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } + + + // Small utility methods. + + /** + * Returns whether the given class has a new name. + */ + private boolean hasName(Clazz clazz) + { + return ClassObfuscator.newClassName(clazz) != null; + } + + + /** + * Returns whether the given method has a new name. + */ + private boolean hasName(Clazz clazz, Method method) + { + return + hasName(method) || + (hasName(clazz) && + method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)); + } + + + /** + * Returns whether the given class member has a new name. + */ + private boolean hasName(Member member) + { + return MemberObfuscator.newMemberName(member) != null; + } +} diff --git a/src/proguard/obfuscate/MemberObfuscator.java b/src/proguard/obfuscate/MemberObfuscator.java new file mode 100644 index 000000000..adf590c5d --- /dev/null +++ b/src/proguard/obfuscate/MemberObfuscator.java @@ -0,0 +1,230 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +/** + * 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. + *

+ * The class members must have been linked before applying this visitor. + * + * @see MethodLinker + * + * @author Eric Lafortune + */ +public class MemberObfuscator +extends SimplifiedVisitor +implements MemberVisitor +{ + private final boolean allowAggressiveOverloading; + private final NameFactory nameFactory; + private final Map descriptorMap; + + + /** + * Creates a new MemberObfuscator. + * @param allowAggressiveOverloading a flag that specifies whether class + * members can be overloaded aggressively. + * @param nameFactory the factory that can produce + * obfuscated member names. + * @param descriptorMap the map of descriptors to + * [new name - old name] maps. + */ + public MemberObfuscator(boolean allowAggressiveOverloading, + NameFactory nameFactory, + Map descriptorMap) + { + this.allowAggressiveOverloading = allowAggressiveOverloading; + this.nameFactory = nameFactory; + this.descriptorMap = descriptorMap; + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + // Special cases: and are always kept unchanged. + // We can ignore them here. + String name = member.getName(clazz); + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + return; + } + + // Get the member's descriptor. + String descriptor = member.getDescriptor(clazz); + + // Check whether we're allowed to do aggressive overloading + 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 = retrieveNameMap(descriptorMap, descriptor); + + // Get the member's new name. + String newName = newMemberName(member); + + // Assign a new one, if necessary. + if (newName == null) + { + // Find an acceptable new name. + nameFactory.reset(); + + do + { + newName = nameFactory.nextName(); + } + while (nameMap.containsKey(newName)); + + // Remember not to use the new name again in this name space. + nameMap.put(newName, name); + + // Assign the new name. + setNewMemberName(member, newName); + } + } + + + // Small utility methods. + + /** + * Gets the name map, based on the given map and a given descriptor. + * A new empty map is created if necessary. + * @param descriptorMap the map of descriptors to [new name - old name] maps. + * @param descriptor the class member descriptor. + * @return the corresponding name map. + */ + static Map retrieveNameMap(Map descriptorMap, String descriptor) + { + // See if we can find the nested map with this descriptor key. + Map nameMap = (Map)descriptorMap.get(descriptor); + + // Create a new one if not. + if (nameMap == null) + { + nameMap = new HashMap(); + descriptorMap.put(descriptor, nameMap); + } + + return nameMap; + } + + + /** + * Assigns a fixed new name to the given class member. + * @param member the class member. + * @param name the new name. + */ + static void setFixedNewMemberName(Member member, String name) + { + VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member); + + if (!(lastVisitorAccepter instanceof LibraryMember) && + !(lastVisitorAccepter instanceof MyFixedName)) + { + lastVisitorAccepter.setVisitorInfo(new MyFixedName(name)); + } + else + { + lastVisitorAccepter.setVisitorInfo(name); + } + } + + + /** + * Assigns a new name to the given class member. + * @param member the class member. + * @param name the new name. + */ + static void setNewMemberName(Member member, String name) + { + MethodLinker.lastVisitorAccepter(member).setVisitorInfo(name); + } + + + /** + * Returns whether the new name of the given class member is fixed. + * @param member the class member. + * @return whether its new name is fixed. + */ + static boolean hasFixedNewMemberName(Member member) + { + VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member); + + return lastVisitorAccepter instanceof LibraryMember || + lastVisitorAccepter instanceof MyFixedName; + } + + + /** + * Retrieves the new name of the given class member. + * @param member the class member. + * @return the class member's new name, or null if it doesn't + * have one yet. + */ + static String newMemberName(Member member) + { + return (String)MethodLinker.lastVisitorAccepter(member).getVisitorInfo(); + } + + + /** + * This VisitorAccepter can be used to wrap a name string, to indicate that + * the name is fixed. + */ + private static class MyFixedName implements VisitorAccepter + { + private String newName; + + + public MyFixedName(String newName) + { + this.newName = newName; + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return newName; + } + + + public void setVisitorInfo(Object visitorInfo) + { + newName = (String)visitorInfo; + } + } +} diff --git a/src/proguard/obfuscate/MemberSpecialNameFilter.java b/src/proguard/obfuscate/MemberSpecialNameFilter.java new file mode 100644 index 000000000..0eb4d2d4e --- /dev/null +++ b/src/proguard/obfuscate/MemberSpecialNameFilter.java @@ -0,0 +1,101 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 MemberVisitor delegates its visits to another given + * MemberVisitor, but only when the visited member has a + * special new name. A special name is a name that might have been produced by + * a SpecialNameFactory. + * + * @see MemberObfuscator + * @see SpecialNameFactory + * + * @author Eric Lafortune + */ +public class MemberSpecialNameFilter implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new MemberSpecialNameFilter. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. + */ + public MemberSpecialNameFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (hasSpecialName(programField)) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (hasSpecialName(programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (hasSpecialName(libraryField)) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (hasSpecialName(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 hasSpecialName(Member member) + { + return SpecialNameFactory.isSpecialName(MemberObfuscator.newMemberName(member)); + } +} diff --git a/src/proguard/obfuscate/MultiMappingProcessor.java b/src/proguard/obfuscate/MultiMappingProcessor.java new file mode 100644 index 000000000..051260f42 --- /dev/null +++ b/src/proguard/obfuscate/MultiMappingProcessor.java @@ -0,0 +1,96 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This MappingKeeper delegates all method calls to each MappingProcessor + * in a given list. + * + * @author Eric Lafortune + */ +public class MultiMappingProcessor implements MappingProcessor +{ + private final MappingProcessor[] mappingProcessors; + + + /** + * Creates a new MultiMappingProcessor. + * @param mappingProcessors the mapping processors to which method calls + * will be delegated. + */ + public MultiMappingProcessor(MappingProcessor[] mappingProcessors) + { + this.mappingProcessors = mappingProcessors; + } + + + // Implementations for MappingProcessor. + + public boolean processClassMapping(String className, + String newClassName) + { + boolean result = false; + + for (int index = 0; index < mappingProcessors.length; index++) + { + result |= mappingProcessors[index].processClassMapping(className, + newClassName); + } + + return result; + } + + + public void processFieldMapping(String className, + String fieldType, + String fieldName, + String newFieldName) + { + for (int index = 0; index < mappingProcessors.length; index++) + { + mappingProcessors[index].processFieldMapping(className, + fieldType, + fieldName, + newFieldName); + } + } + + + public void processMethodMapping(String className, + int firstLineNumber, + int lastLineNumber, + String methodReturnType, + String methodName, + String methodArguments, + String newMethodName) + { + for (int index = 0; index < mappingProcessors.length; index++) + { + mappingProcessors[index].processMethodMapping(className, + firstLineNumber, + lastLineNumber, + methodReturnType, + methodName, + methodArguments, + newMethodName); + } + } +} diff --git a/src/proguard/obfuscate/NameFactory.java b/src/proguard/obfuscate/NameFactory.java new file mode 100644 index 000000000..97ebe5af1 --- /dev/null +++ b/src/proguard/obfuscate/NameFactory.java @@ -0,0 +1,34 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This interfaces provides methods to generate unique sequences of names. + * The names must be valid Java identifiers. + * + * @author Eric Lafortune + */ +public interface NameFactory +{ + public void reset(); + + public String nextName(); +} diff --git a/src/proguard/obfuscate/NameFactoryResetter.java b/src/proguard/obfuscate/NameFactoryResetter.java new file mode 100644 index 000000000..b04d12e94 --- /dev/null +++ b/src/proguard/obfuscate/NameFactoryResetter.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassVisitor; + +/** + * This ClassVisitor resets a given name factory whenever it visits a class + * file. + * + * @author Eric Lafortune + */ +public class NameFactoryResetter implements ClassVisitor +{ + private final NameFactory nameFactory; + + + /** + * Creates a new NameFactoryResetter. + * @param nameFactory the name factory to be reset. + */ + public NameFactoryResetter(NameFactory nameFactory) + { + this.nameFactory = nameFactory; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + nameFactory.reset(); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + nameFactory.reset(); + } +} diff --git a/src/proguard/obfuscate/NameMarker.java b/src/proguard/obfuscate/NameMarker.java new file mode 100644 index 000000000..5283ef342 --- /dev/null +++ b/src/proguard/obfuscate/NameMarker.java @@ -0,0 +1,166 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.visitor.*; + + +/** + * This ClassVisitor and MemberVisitor + * marks names of the classes and class members it visits. The marked names + * will remain unchanged in the obfuscation step. + * + * @see ClassObfuscator + * @see MemberObfuscator + * + * @author Eric Lafortune + */ +class NameMarker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor, + InnerClassesInfoVisitor, + ConstantVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + keepClassName(programClass); + + // Make sure any outer class names are kept as well. + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + keepClassName(libraryClass); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + keepFieldName(programClass, programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + keepMethodName(programClass, programMethod); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + keepFieldName(libraryClass, libraryField); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + keepMethodName(libraryClass, libraryMethod); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Make sure the outer class names are kept as well. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + // Make sure the outer class name is kept as well. + 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 name is kept as well. + classConstant.referencedClassAccept(this); + } + + + // Small utility method. + + /** + * Ensures the name of the given class name will be kept. + */ + public void keepClassName(Clazz clazz) + { + ClassObfuscator.setNewClassName(clazz, + clazz.getName()); + } + + + /** + * Ensures the name of the given field name will be kept. + */ + private void keepFieldName(Clazz clazz, Field field) + { + MemberObfuscator.setFixedNewMemberName(field, + field.getName(clazz)); + } + + + /** + * Ensures the name of the given method name will be kept. + */ + private void keepMethodName(Clazz clazz, Method method) + { + String name = method.getName(clazz); + + if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) && + !name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + MemberObfuscator.setFixedNewMemberName(method, + method.getName(clazz)); + } + } +} diff --git a/src/proguard/obfuscate/NumericNameFactory.java b/src/proguard/obfuscate/NumericNameFactory.java new file mode 100644 index 000000000..b1e38b113 --- /dev/null +++ b/src/proguard/obfuscate/NumericNameFactory.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.util.*; + + +/** + * This NameFactory generates unique numeric names, starting at + * "1". + * + * @author Eric Lafortune + */ +public class NumericNameFactory implements NameFactory +{ + private int index; + + + // Implementations for NameFactory. + + public void reset() + { + index = 0; + } + + + public String nextName() + { + return Integer.toString(++index); + } +} \ No newline at end of file diff --git a/src/proguard/obfuscate/Obfuscator.java b/src/proguard/obfuscate/Obfuscator.java new file mode 100644 index 000000000..1bbfc52b7 --- /dev/null +++ b/src/proguard/obfuscate/Obfuscator.java @@ -0,0 +1,454 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.obfuscate; + +import proguard.*; +import proguard.classfile.*; +import proguard.classfile.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.*; + +/** + * This class can perform obfuscation of class pools according to a given + * specification. + * + * @author Eric Lafortune + */ +public class Obfuscator +{ + private final Configuration configuration; + + + /** + * Creates a new Obfuscator. + */ + public Obfuscator(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Performs obfuscation of the given program class pool. + */ + public void execute(ClassPool programClassPool, + ClassPool libraryClassPool) throws IOException + { + // Check if we have at least some keep commands. + if (configuration.keep == null && + configuration.applyMapping == null && + configuration.printMapping == null) + { + throw new IOException("You have to specify '-keep' options for the obfuscation step."); + } + + // Clean up any old visitor info. + programClassPool.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. + ClassVisitor memberInfoLinker = + configuration.useUniqueClassMemberNames ? + (ClassVisitor)new AllMemberVisitor(new MethodLinker()) : + (ClassVisitor)new BottomClassFilter(new MethodLinker()); + + programClassPool.classesAccept(memberInfoLinker); + libraryClassPool.classesAccept(memberInfoLinker); + + // Create a visitor for marking the seeds. + NameMarker nameMarker = new NameMarker(); + ClassPoolVisitor classPoolvisitor = + 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.classesAccept(nameMarker); + libraryClassPool.classesAccept(new AllMemberVisitor(nameMarker)); + + // Mark attributes that have to be kept. + AttributeVisitor attributeUsageMarker = + new NonEmptyAttributeFilter( + new AttributeUsageMarker()); + + AttributeVisitor optionalAttributeUsageMarker = + configuration.keepAttributes == null ? null : + new AttributeNameFilter(new ListParser(new NameParser()).parse(configuration.keepAttributes), + attributeUsageMarker); + + programClassPool.classesAccept( + new AllAttributeVisitor(true, + new RequiredAttributeFilter(attributeUsageMarker, + optionalAttributeUsageMarker))); + + // Keep parameter names and types if specified. + if (configuration.keepParameterNames) + { + programClassPool.classesAccept( + new AllMethodVisitor( + new MemberNameFilter( + new AllAttributeVisitor(true, + new ParameterNameMarker(attributeUsageMarker))))); + } + + // Remove the attributes that can be discarded. Note that the attributes + // may only be discarded after the seeds have been marked, since the + // configuration may rely on annotations. + programClassPool.classesAccept(new AttributeShrinker()); + + // Apply the mapping, if one has been specified. The mapping can + // override the names of library classes and of library class members. + if (configuration.applyMapping != null) + { + WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn); + + MappingReader reader = new MappingReader(configuration.applyMapping); + + MappingProcessor keeper = + new MultiMappingProcessor(new MappingProcessor[] + { + new MappingKeeper(programClassPool, warningPrinter), + new MappingKeeper(libraryClassPool, null), + }); + + reader.pump(keeper); + + // Print out a summary of the warnings if necessary. + int mappingWarningCount = warningPrinter.getWarningCount(); + if (mappingWarningCount > 0) + { + System.err.println("Warning: there were " + mappingWarningCount + + " kept classes and class members that were remapped anyway."); + System.err.println(" You should adapt your configuration or edit the mapping file."); + + if (!configuration.ignoreWarnings) + { + System.err.println(" If you are sure this remapping won't hurt,"); + System.err.println(" you could try your luck using the '-ignorewarnings' option."); + throw new IOException("Please correct the above warnings first."); + } + } + } + + // Come up with new names for all classes. + DictionaryNameFactory classNameFactory = configuration.classObfuscationDictionary != null ? + new DictionaryNameFactory(configuration.classObfuscationDictionary, null) : + null; + + DictionaryNameFactory packageNameFactory = configuration.packageObfuscationDictionary != null ? + new DictionaryNameFactory(configuration.packageObfuscationDictionary, null) : + null; + + programClassPool.classesAccept( + new ClassObfuscator(programClassPool, + classNameFactory, + packageNameFactory, + configuration.useMixedCaseClassNames, + configuration.keepPackageNames, + configuration.flattenPackageHierarchy, + configuration.repackageClasses, + configuration.allowAccessModification)); + + // Come up with new names for all class members. + NameFactory nameFactory = new SimpleNameFactory(); + + if (configuration.obfuscationDictionary != null) + { + nameFactory = new DictionaryNameFactory(configuration.obfuscationDictionary, + nameFactory); + } + + WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn); + + // Maintain a map of names to avoid [descriptor - new name - old name]. + Map descriptorMap = new HashMap(); + + // Do the class member names have to be globally unique? + if (configuration.useUniqueClassMemberNames) + { + // Collect all member names in all classes. + programClassPool.classesAccept( + new AllMemberVisitor( + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap))); + + // Assign new names to all members in all classes. + programClassPool.classesAccept( + new AllMemberVisitor( + new MemberObfuscator(configuration.overloadAggressively, + nameFactory, + descriptorMap))); + } + else + { + // Come up with new names for all non-private class members. + programClassPool.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + // Collect all private member names in this class and down + // the hierarchy. + 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 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 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.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + // Collect all member names in this class. + new AllMemberVisitor( + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)), + + // Collect all non-private member names higher up the hierarchy. + new ClassHierarchyTraveler(false, true, true, false, + new AllMemberVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)))), + + // Collect all member names from interfaces of abstract + // classes down the hierarchy. + // Due to an error in the JLS/JVMS, virtual invocations + // may end up at a private method otherwise (Sun/Oracle + // bugs #6691741 and #6684387, ProGuard bug #3471941, + // and ProGuard test #1180). + new ClassHierarchyTraveler(false, false, false, true, + new ClassAccessFilter(ClassConstants.INTERNAL_ACC_ABSTRACT, 0, + new ClassHierarchyTraveler(false, false, true, false, + new AllMemberVisitor( + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap))))), + + // Assign new names to all private members in this class. + new AllMemberVisitor( + new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0, + new MemberObfuscator(configuration.overloadAggressively, + nameFactory, + descriptorMap))), + + // Clear the collected names. + new MapCleaner(descriptorMap) + })); + } + + // Some class members may have ended up with conflicting names. + // Come up with new, globally unique names for them. + NameFactory specialNameFactory = + new SpecialNameFactory(new SimpleNameFactory()); + + // Collect a map of special names to avoid + // [descriptor - new name - old name]. + Map specialDescriptorMap = new HashMap(); + + programClassPool.classesAccept( + new AllMemberVisitor( + new MemberSpecialNameFilter( + new MemberNameCollector(configuration.overloadAggressively, + specialDescriptorMap)))); + + libraryClassPool.classesAccept( + new AllMemberVisitor( + new MemberSpecialNameFilter( + new MemberNameCollector(configuration.overloadAggressively, + specialDescriptorMap)))); + + // Replace conflicting non-private member names with special names. + programClassPool.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + // Collect all private member names in this class and down + // the hierarchy. + 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) + })); + + // Replace conflicting private member names with special names. + // This is only possible if those names were kept or mapped. + programClassPool.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + // Collect all member names in this class. + new AllMemberVisitor( + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap)), + + // Collect all non-private member names higher up the hierarchy. + 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 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. + int warningCount = warningPrinter.getWarningCount(); + if (warningCount > 0) + { + System.err.println("Warning: there were " + warningCount + + " conflicting class member name mappings."); + System.err.println(" Your configuration may be inconsistent."); + + if (!configuration.ignoreWarnings) + { + System.err.println(" If you are sure the conflicts are harmless,"); + System.err.println(" you could try your luck using the '-ignorewarnings' option."); + throw new IOException("Please correct the above warnings first."); + } + } + + // Print out the mapping, if requested. + if (configuration.printMapping != null) + { + PrintStream ps = + configuration.printMapping == Configuration.STD_OUT ? System.out : + new PrintStream( + new BufferedOutputStream( + new FileOutputStream(configuration.printMapping))); + + // Print out items that will be removed. + programClassPool.classesAcceptAlphabetically(new MappingPrinter(ps)); + + if (ps == System.out) + { + ps.flush(); + } + else + { + ps.close(); + } + } + + // Actually apply the new names. + programClassPool.classesAccept(new ClassRenamer()); + libraryClassPool.classesAccept(new ClassRenamer()); + + // Update all references to these new names. + 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.classesAccept( + new AllConstantVisitor( + new AccessFixer())); + + // Fix the access flags of the inner classes information. + programClassPool.classesAccept( + new AllAttributeVisitor( + new AllInnerClassesInfoVisitor( + new InnerClassesAccessFixer()))); + } + + // Fix the bridge method flags. + programClassPool.classesAccept( + new AllMethodVisitor( + new BridgeMethodFixer())); + + // Rename the source file attributes, if requested. + if (configuration.newSourceFileAttribute != null) + { + programClassPool.classesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute)); + } + + // Remove unused constants. + programClassPool.classesAccept( + new ConstantPoolShrinker()); + } +} diff --git a/src/proguard/obfuscate/ParameterNameMarker.java b/src/proguard/obfuscate/ParameterNameMarker.java new file mode 100644 index 000000000..22af12522 --- /dev/null +++ b/src/proguard/obfuscate/ParameterNameMarker.java @@ -0,0 +1,128 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.AttributeVisitor; +import proguard.classfile.util.*; + +/** + * This AttributeVisitor trims and marks all local variable (type) table + * attributes that it visits. It keeps parameter names and types and removes + * the ordinary local variable names and types. + * + * @author Eric Lafortune + */ +public class ParameterNameMarker +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final AttributeVisitor attributeUsageMarker; + + + /** + * Constructs a new ParameterNameMarker. + * @param attributeUsageMarker the marker that will be used to mark + * attributes containing local variable info. + */ + public ParameterNameMarker(AttributeVisitor attributeUsageMarker) + { + this.attributeUsageMarker = attributeUsageMarker; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + if (!AttributeUsageMarker.isUsed(localVariableTableAttribute) && + hasParameters(clazz, method)) + { + // Shift the entries that start at offset 0 to the front. + int newIndex = 0; + + for (int index = 0; index < localVariableTableAttribute.u2localVariableTableLength; index++) + { + LocalVariableInfo localVariableInfo = + localVariableTableAttribute.localVariableTable[index]; + + if (localVariableInfo.u2startPC == 0) + { + localVariableTableAttribute.localVariableTable[newIndex++] = + localVariableInfo; + } + } + + // Trim the table. + localVariableTableAttribute.u2localVariableTableLength = newIndex; + + // Mark the table if there are any entries. + if (newIndex > 0) + { + attributeUsageMarker.visitLocalVariableTableAttribute(clazz, method, codeAttribute, localVariableTableAttribute); + } + } + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + if (!AttributeUsageMarker.isUsed(localVariableTypeTableAttribute) && + hasParameters(clazz, method)) + { + // Shift the entries that start at offset 0 to the front. + int newIndex = 0; + + for (int index = 0; index < localVariableTypeTableAttribute.u2localVariableTypeTableLength; index++) + { + LocalVariableTypeInfo localVariableTypeInfo = + localVariableTypeTableAttribute.localVariableTypeTable[index]; + + if (localVariableTypeInfo.u2startPC == 0) + { + localVariableTypeTableAttribute.localVariableTypeTable[newIndex++] = + localVariableTypeInfo; + } + } + + // Trim the table. + localVariableTypeTableAttribute.u2localVariableTypeTableLength = newIndex; + + // Mark the table if there are any entries. + if (newIndex > 0) + { + attributeUsageMarker.visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, localVariableTypeTableAttribute); + } + } + } + + + // Small utility methods. + + private boolean hasParameters(Clazz clazz, Method method) + { + return method.getDescriptor(clazz).charAt(1) != ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE; + } +} \ No newline at end of file diff --git a/src/proguard/obfuscate/SimpleNameFactory.java b/src/proguard/obfuscate/SimpleNameFactory.java new file mode 100644 index 000000000..0473852d5 --- /dev/null +++ b/src/proguard/obfuscate/SimpleNameFactory.java @@ -0,0 +1,156 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 java.util.*; + + +/** + * This NameFactory generates unique short names, using mixed-case + * characters or lower-case characters only. + * + * @author Eric Lafortune + */ +public class SimpleNameFactory implements NameFactory +{ + private static final int CHARACTER_COUNT = 26; + + private static final List cachedMixedCaseNames = new ArrayList(); + private static final List cachedLowerCaseNames = new ArrayList(); + + private final boolean generateMixedCaseNames; + private int index = 0; + + + /** + * Creates a new SimpleNameFactory that generates mixed-case names. + */ + public SimpleNameFactory() + { + this(true); + } + + + /** + * Creates a new SimpleNameFactory. + * @param generateMixedCaseNames a flag to indicate whether the generated + * names will be mixed-case, or lower-case only. + */ + public SimpleNameFactory(boolean generateMixedCaseNames) + { + this.generateMixedCaseNames = generateMixedCaseNames; + } + + + // Implementations for NameFactory. + + public void reset() + { + index = 0; + } + + + public String nextName() + { + return name(index++); + } + + + /** + * Returns the name at the given index. + */ + private String name(int index) + { + // Which cache do we need? + List cachedNames = generateMixedCaseNames ? + cachedMixedCaseNames : + cachedLowerCaseNames; + + // Do we have the name in the cache? + if (index < cachedNames.size()) + { + return (String)cachedNames.get(index); + } + + // Create a new name and cache it. + String name = newName(index); + cachedNames.add(index, name); + + return name; + } + + + /** + * Creates and returns the name at the given index. + */ + private String newName(int index) + { + // If we're allowed to generate mixed-case names, we can use twice as + // many characters. + int totalCharacterCount = generateMixedCaseNames ? + 2 * CHARACTER_COUNT : + CHARACTER_COUNT; + + int baseIndex = index / totalCharacterCount; + int offset = index % totalCharacterCount; + + char newChar = charAt(offset); + + String newName = baseIndex == 0 ? + new String(new char[] { newChar }) : + (name(baseIndex-1) + newChar); + + return newName; + } + + + /** + * Returns the character with the given index, between 0 and the number of + * acceptable characters. + */ + private char charAt(int index) + { + return (char)((index < CHARACTER_COUNT ? 'a' - 0 : + 'A' - CHARACTER_COUNT) + index); + } + + + public static void main(String[] args) + { + System.out.println("Some mixed-case names:"); + printNameSamples(new SimpleNameFactory(true), 60); + System.out.println("Some lower-case names:"); + printNameSamples(new SimpleNameFactory(false), 60); + System.out.println("Some more mixed-case names:"); + printNameSamples(new SimpleNameFactory(true), 80); + System.out.println("Some more lower-case names:"); + printNameSamples(new SimpleNameFactory(false), 80); + } + + + private static void printNameSamples(SimpleNameFactory factory, int count) + { + for (int counter = 0; counter < count; counter++) + { + System.out.println(" ["+factory.nextName()+"]"); + } + } +} diff --git a/src/proguard/obfuscate/SourceFileRenamer.java b/src/proguard/obfuscate/SourceFileRenamer.java new file mode 100644 index 000000000..248d18f51 --- /dev/null +++ b/src/proguard/obfuscate/SourceFileRenamer.java @@ -0,0 +1,84 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.AttributeVisitor; +import proguard.classfile.editor.ConstantPoolEditor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * 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 +extends SimplifiedVisitor +implements ClassVisitor, + AttributeVisitor +{ + private final String newSourceFileAttribute; + + + /** + * Creates a new SourceFileRenamer. + * @param newSourceFileAttribute the new string to be put in the source file + * attributes. + */ + public SourceFileRenamer(String newSourceFileAttribute) + { + this.newSourceFileAttribute = newSourceFileAttribute; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Only visit the class attributes. + programClass.attributesAccept(this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + // Fix the source file attribute. + sourceFileAttribute.u2sourceFileIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSourceFileAttribute); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + // Fix the source file attribute. + sourceDirAttribute.u2sourceDirIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSourceFileAttribute); + } +} diff --git a/src/proguard/obfuscate/SpecialNameFactory.java b/src/proguard/obfuscate/SpecialNameFactory.java new file mode 100644 index 000000000..596f91924 --- /dev/null +++ b/src/proguard/obfuscate/SpecialNameFactory.java @@ -0,0 +1,83 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * This NameFactory generates names that are special, by appending + * a suffix. + * + * @author Eric Lafortune + */ +public class SpecialNameFactory implements NameFactory +{ + private static final char SPECIAL_SUFFIX = '_'; + + + private final NameFactory nameFactory; + + + /** + * Creates a new SpecialNameFactory. + * @param nameFactory the name factory from which original names will be + * retrieved. + */ + public SpecialNameFactory(NameFactory nameFactory) + { + this.nameFactory = nameFactory; + } + + + // Implementations for NameFactory. + + public void reset() + { + nameFactory.reset(); + } + + + public String nextName() + { + return nameFactory.nextName() + SPECIAL_SUFFIX; + } + + + // Small utility methods. + + /** + * Returns whether the given name is special. + */ + static boolean isSpecialName(String name) + { + return name != null && + name.charAt(name.length()-1) == SPECIAL_SUFFIX; + } + + + public static void main(String[] args) + { + SpecialNameFactory factory = new SpecialNameFactory(new SimpleNameFactory()); + + for (int counter = 0; counter < 50; counter++) + { + System.out.println("["+factory.nextName()+"]"); + } + } +} diff --git a/src/proguard/obfuscate/package.html b/src/proguard/obfuscate/package.html new file mode 100644 index 000000000..a82d26681 --- /dev/null +++ b/src/proguard/obfuscate/package.html @@ -0,0 +1,3 @@ + +This package contains classes to perform obfuscation of class files. + diff --git a/src/proguard/optimize/BootstrapMethodArgumentShrinker.java b/src/proguard/optimize/BootstrapMethodArgumentShrinker.java new file mode 100644 index 000000000..26f134954 --- /dev/null +++ b/src/proguard/optimize/BootstrapMethodArgumentShrinker.java @@ -0,0 +1,103 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.ConstantPoolEditor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; +import proguard.optimize.info.*; +import proguard.optimize.peephole.VariableShrinker; + +/** + * This BootstrapMethodInfoVisitor removes unused constant arguments from + * bootstrap method entries that it visits. + * + * @see ParameterUsageMarker + * @see VariableUsageMarker + * @see VariableShrinker + * @author Eric Lafortune + */ +public class BootstrapMethodArgumentShrinker +extends SimplifiedVisitor +implements BootstrapMethodInfoVisitor, + ConstantVisitor, + MemberVisitor +{ + private long usedParameters; + + + // Implementations for BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + // Check which method parameters are used. + usedParameters = -1L; + clazz.constantPoolEntryAccept(bootstrapMethodInfo.u2methodHandleIndex, this); + + // Remove the unused arguments. + int methodArgumentCount = bootstrapMethodInfo.u2methodArgumentCount; + int[] methodArguments = bootstrapMethodInfo.u2methodArguments; + + int newArgumentIndex = 0; + + for (int argumentIndex = 0; argumentIndex < methodArgumentCount; argumentIndex++) + { + if (argumentIndex >= 64 || + (usedParameters & (1L << argumentIndex)) != 0L) + { + methodArguments[newArgumentIndex++] = methodArguments[argumentIndex]; + } + } + + // Update the number of arguments. + bootstrapMethodInfo.u2methodArgumentCount = newArgumentIndex; + } + + + // Implementations for ConstantVisitor. + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + // Check the referenced bootstrap method. + clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, this); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + // Check the referenced class member itself. + refConstant.referencedMemberAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + usedParameters = ParameterUsageMarker.getUsedParameters(programMethod); + } +} diff --git a/src/proguard/optimize/ChangedCodePrinter.java b/src/proguard/optimize/ChangedCodePrinter.java new file mode 100644 index 000000000..668d43de9 --- /dev/null +++ b/src/proguard/optimize/ChangedCodePrinter.java @@ -0,0 +1,297 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.attribute.preverification.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.ClassUtil; + +/** + * 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 AttributeVisitor +{ + 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 visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + attributeVisitor.visitBootstrapMethodsAttribute(clazz, bootstrapMethodsAttribute); + } + + + 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 void visitSignatureAttribute(Clazz clazz, SignatureAttribute syntheticAttribute) + { + attributeVisitor.visitSignatureAttribute(clazz, syntheticAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) + { + attributeVisitor.visitDeprecatedAttribute(clazz, field, deprecatedAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) + { + attributeVisitor.visitSyntheticAttribute(clazz, field, syntheticAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute syntheticAttribute) + { + attributeVisitor.visitSignatureAttribute(clazz, field, syntheticAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) + { + attributeVisitor.visitDeprecatedAttribute(clazz, method, deprecatedAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) + { + attributeVisitor.visitSyntheticAttribute(clazz, method, syntheticAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute syntheticAttribute) + { + attributeVisitor.visitSignatureAttribute(clazz, method, syntheticAttribute); + } + + + 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) + { + attributeVisitor.visitStackMapAttribute(clazz, method, codeAttribute, stackMapAttribute); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + attributeVisitor.visitStackMapTableAttribute(clazz, method, codeAttribute, stackMapTableAttribute); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + attributeVisitor.visitLineNumberTableAttribute(clazz, method, codeAttribute, lineNumberTableAttribute); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + attributeVisitor.visitLocalVariableTableAttribute(clazz, method, codeAttribute, localVariableTableAttribute); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + attributeVisitor.visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, localVariableTypeTableAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, field, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, field, runtimeInvisibleAnnotationsAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, method, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, method, runtimeInvisibleAnnotationsAttribute); + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + attributeVisitor.visitRuntimeVisibleParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + attributeVisitor.visitRuntimeInvisibleParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + attributeVisitor.visitAnnotationDefaultAttribute(clazz, method, annotationDefaultAttribute); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + byte[] code = codeAttribute.code; + byte[] oldCode = new byte[code.length]; + + // Copy the current code. + System.arraycopy(code, 0, oldCode, 0, codeAttribute.u4codeLength); + + // Delegate to the real visitor. + attributeVisitor.visitCodeAttribute(clazz, method, codeAttribute); + + // Check if the code has changed. + if (codeHasChanged(codeAttribute, oldCode)) + { + printChangedCode(clazz, method, codeAttribute, oldCode); + } + } + + + // Small utility methods. + + private boolean codeHasChanged(CodeAttribute codeAttribute, byte[] oldCode) + { + if (oldCode.length != codeAttribute.u4codeLength) + { + return true; + } + + for (int index = 0; index < codeAttribute.u4codeLength; index++) + { + if (oldCode[index] != codeAttribute.code[index]) + { + return true; + } + } + + return false; + } + + + private void printChangedCode(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + byte[] oldCode) + { + 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 < codeAttribute.u4codeLength; index++) + { + System.out.println( + (oldCode[index] == codeAttribute.code[index]? " -- ":" => ")+ + index+": "+ + 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 000000000..1f30a302b --- /dev/null +++ b/src/proguard/optimize/ConstantMemberFilter.java @@ -0,0 +1,77 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; +import proguard.classfile.visitor.MemberVisitor; +import proguard.evaluation.value.Value; +import proguard.optimize.evaluation.StoringInvocationUnit; + +/** + * This MemberVisitor delegates its visits to program class members + * to another given MemberVisitor, but only when the visited + * class member 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 MemberVisitor 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.isParticular()) + { + constantMemberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + Value value = StoringInvocationUnit.getMethodReturnValue(programMethod); + if (value != null && + value.isParticular()) + { + constantMemberVisitor.visitProgramMethod(programClass, programMethod); + } + } +} diff --git a/src/proguard/optimize/ConstantParameterFilter.java b/src/proguard/optimize/ConstantParameterFilter.java new file mode 100644 index 000000000..1500fd0a9 --- /dev/null +++ b/src/proguard/optimize/ConstantParameterFilter.java @@ -0,0 +1,79 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize; + +import proguard.classfile.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; +import proguard.evaluation.value.Value; +import proguard.optimize.evaluation.StoringInvocationUnit; +import proguard.optimize.info.ParameterUsageMarker; + +/** + * This MemberVisitor delegates its visits to program methods + * to another given MemberVisitor, for each method parameter + * that has been marked as constant. + * + * @see StoringInvocationUnit + * @author Eric Lafortune + */ +public class ConstantParameterFilter +extends SimplifiedVisitor +implements MemberVisitor +{ + private final MemberVisitor constantParameterVisitor; + + + /** + * Creates a new ConstantParameterFilter. + * @param constantParameterVisitor the MemberVisitor to which + * visits will be delegated. + */ + public ConstantParameterFilter(MemberVisitor constantParameterVisitor) + { + this.constantParameterVisitor = constantParameterVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // All parameters of non-static methods are shifted by one in the local + // variable frame. + int firstParameterIndex = + (programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? + 0 : 1; + + int parameterCount = + ClassUtil.internalMethodParameterCount(programMethod.getDescriptor(programClass)); + + for (int index = firstParameterIndex; index < parameterCount; index++) + { + Value value = StoringInvocationUnit.getMethodParameterValue(programMethod, index); + if (value != null && + value.isParticular()) + { + constantParameterVisitor.visitProgramMethod(programClass, programMethod); + } + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/DuplicateInitializerFixer.java b/src/proguard/optimize/DuplicateInitializerFixer.java new file mode 100644 index 000000000..95bc2f10f --- /dev/null +++ b/src/proguard/optimize/DuplicateInitializerFixer.java @@ -0,0 +1,215 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.editor.ConstantPoolEditor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; + +/** + * This MemberVisitor adds an additional parameter to the duplicate + * initialization methods that it visits. + */ +public class DuplicateInitializerFixer +extends SimplifiedVisitor +implements MemberVisitor, + AttributeVisitor +{ + private static final boolean DEBUG = false; + + private static final char[] TYPES = new char[] + { + ClassConstants.INTERNAL_TYPE_BYTE, + ClassConstants.INTERNAL_TYPE_CHAR, + ClassConstants.INTERNAL_TYPE_SHORT, + ClassConstants.INTERNAL_TYPE_INT, + ClassConstants.INTERNAL_TYPE_BOOLEAN + }; + + + private final MemberVisitor extraFixedInitializerVisitor; + + + /** + * Creates a new DuplicateInitializerFixer. + */ + public DuplicateInitializerFixer() + { + this(null); + } + + + /** + * Creates a new DuplicateInitializerFixer with an extra visitor. + * @param extraFixedInitializerVisitor an optional extra visitor for all + * initializers that have been fixed. + */ + public DuplicateInitializerFixer(MemberVisitor extraFixedInitializerVisitor) + { + this.extraFixedInitializerVisitor = extraFixedInitializerVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Is it a class instance initializer? + String name = programMethod.getName(programClass); + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + // Is there already another initializer with the same descriptor? + String descriptor = programMethod.getDescriptor(programClass); + Method similarMethod = programClass.findMethod(name, descriptor); + if (!programMethod.equals(similarMethod)) + { + // Should this initializer be preserved? + if (KeepMarker.isKept(programMethod)) + { + // Fix the other initializer. + programMethod = (ProgramMethod)similarMethod; + } + + int index = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + + // Try to find a new, unique descriptor. + int typeCounter = 0; + while (true) + { + // Construct the new descriptor by inserting a new type + // as an additional last argument. + StringBuffer newDescriptorBuffer = + new StringBuffer(descriptor.substring(0, index)); + + for (int arrayDimension = 0; arrayDimension < typeCounter / TYPES.length; arrayDimension++) + { + newDescriptorBuffer.append(ClassConstants.INTERNAL_TYPE_ARRAY); + } + + newDescriptorBuffer.append(TYPES[typeCounter % TYPES.length]); + newDescriptorBuffer.append(descriptor.substring(index)); + + String newDescriptor = newDescriptorBuffer.toString(); + + // Is the new initializer descriptor unique? + if (programClass.findMethod(name, newDescriptor) == null) + { + if (DEBUG) + { + System.out.println("DuplicateInitializerFixer:"); + System.out.println(" ["+programClass.getName()+"."+name+descriptor+"] ("+ClassUtil.externalClassAccessFlags(programMethod.getAccessFlags())+") -> ["+newDescriptor+"]"); + } + + // Update the descriptor. + programMethod.u2descriptorIndex = + new ConstantPoolEditor(programClass).addUtf8Constant(newDescriptor); + + // Fix the local variable frame size, the method + // signature, and the parameter annotations, if + // necessary. + programMethod.attributesAccept(programClass, + this); + + // Visit the initializer, if required. + if (extraFixedInitializerVisitor != null) + { + extraFixedInitializerVisitor.visitProgramMethod(programClass, programMethod); + } + + // We're done with this constructor. + return; + } + + typeCounter++; + } + } + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // The minimum variable size is determined by the arguments. + int maxLocals = + ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), + method.getAccessFlags()); + + if (codeAttribute.u2maxLocals < maxLocals) + { + codeAttribute.u2maxLocals = maxLocals; + } + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + String descriptor = method.getDescriptor(clazz); + int descriptorIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + String signature = clazz.getString(signatureAttribute.u2signatureIndex); + int signatureIndex = signature.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + + String newSignature = signature.substring(0, signatureIndex) + + descriptor.charAt(descriptorIndex - 1) + + signature.substring(signatureIndex); + + // Update the signature. + signatureAttribute.u2signatureIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Update the number of parameters. + int oldParametersCount = parameterAnnotationsAttribute.u2parametersCount++; + + if (parameterAnnotationsAttribute.u2parameterAnnotationsCount == null || + parameterAnnotationsAttribute.u2parameterAnnotationsCount.length < parameterAnnotationsAttribute.u2parametersCount) + { + int[] annotationsCounts = new int[parameterAnnotationsAttribute.u2parametersCount]; + Annotation[][] annotations = new Annotation[parameterAnnotationsAttribute.u2parametersCount][]; + + System.arraycopy(parameterAnnotationsAttribute.u2parameterAnnotationsCount, + 0, + annotationsCounts, + 0, + oldParametersCount); + + System.arraycopy(parameterAnnotationsAttribute.parameterAnnotations, + 0, + annotations, + 0, + oldParametersCount); + + parameterAnnotationsAttribute.u2parameterAnnotationsCount = annotationsCounts; + parameterAnnotationsAttribute.parameterAnnotations = annotations; + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java new file mode 100644 index 000000000..5edaba0e6 --- /dev/null +++ b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java @@ -0,0 +1,161 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.CodeAttributeEditor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; + +/** + * This AttributeVisitor adds an additional integer parameter to the tweaked + * initialization method invocations that it visits. + */ +public class DuplicateInitializerInvocationFixer +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + MemberVisitor +{ + private static final boolean DEBUG = false; + + private final InstructionVisitor extraAddedInstructionVisitor; + + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + private String descriptor; + private int descriptorLengthDelta; + + + /** + * Creates a new DuplicateInitializerInvocationFixer. + */ + public DuplicateInitializerInvocationFixer() + { + this(null); + } + + + /** + * Creates a new DuplicateInitializerInvocationFixer. + * @param extraAddedInstructionVisitor an optional extra visitor for all + * added instructions. + */ + public DuplicateInitializerInvocationFixer(InstructionVisitor extraAddedInstructionVisitor) + { + this.extraAddedInstructionVisitor = extraAddedInstructionVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + + // Reset the code changes. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // Fix any duplicate constructor invocations. + codeAttribute.instructionsAccept(clazz, + method, + this); + + // Apply all accumulated changes to the code. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + if (constantInstruction.opcode == InstructionConstants.OP_INVOKESPECIAL) + { + descriptorLengthDelta = 0; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + + if (descriptorLengthDelta > 0) + { + Instruction extraInstruction = + new SimpleInstruction(descriptorLengthDelta == 1 ? + InstructionConstants.OP_ICONST_0 : + InstructionConstants.OP_ACONST_NULL); + + codeAttributeEditor.insertBeforeInstruction(offset, + extraInstruction); + + if (DEBUG) + { + System.out.println(" ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] Inserting "+extraInstruction.toString()+" before "+constantInstruction.toString(offset)); + } + + if (extraAddedInstructionVisitor != null) + { + extraInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + } + + + // Implementations for ConstantVisitor. + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + // Check the referenced constructor descriptor. + descriptor = methodrefConstant.getType(clazz); + methodrefConstant.referencedMemberAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + descriptorLengthDelta = + programMethod.getDescriptor(programClass).length() - descriptor.length(); + + if (DEBUG) + { + if (descriptorLengthDelta > 0) + { + System.out.println("DuplicateInitializerInvocationFixer:"); + System.out.println(" ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] ("+ClassUtil.externalClassAccessFlags(programMethod.getAccessFlags())+") referenced by:"); + } + } + } +} diff --git a/src/proguard/optimize/KeepMarker.java b/src/proguard/optimize/KeepMarker.java new file mode 100644 index 000000000..b0eab7b4c --- /dev/null +++ b/src/proguard/optimize/KeepMarker.java @@ -0,0 +1,103 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.MethodLinker; +import proguard.classfile.visitor.*; +import proguard.optimize.info.NoSideEffectMethodMarker; + + +/** + * This ClassVisitor and MemberVisitor + * marks classes and class members it visits. The marked elements + * will remain unchanged as necessary in the optimization step. + * + * @see NoSideEffectMethodMarker + * @author Eric Lafortune + */ +public class KeepMarker +implements ClassVisitor, + MemberVisitor +{ + // A visitor info flag to indicate the visitor accepter is being kept. + private static final Object KEPT = new Object(); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + markAsKept(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + markAsKept(libraryClass); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + markAsKept(programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + markAsKept(MethodLinker.lastMember(programMethod)); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + markAsKept(libraryField); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + markAsKept(MethodLinker.lastMember(libraryMethod)); + } + + + // Small utility methods. + + private static void markAsKept(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(KEPT); + } + + + public static boolean isKept(VisitorAccepter visitorAccepter) + { + // We're also checking for the constant in NoSideEffectMethodMarker, + // to keep things simple. + Object visitorInfo = + MethodLinker.lastVisitorAccepter(visitorAccepter).getVisitorInfo(); + + return visitorInfo == KEPT || + visitorInfo == NoSideEffectMethodMarker.KEPT_BUT_NO_SIDE_EFFECTS; + } +} diff --git a/src/proguard/optimize/KeptClassFilter.java b/src/proguard/optimize/KeptClassFilter.java new file mode 100644 index 000000000..60a9d3e3f --- /dev/null +++ b/src/proguard/optimize/KeptClassFilter.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassVisitor; + +/** + * This ClassVisitor delegates all its method calls to another ClassVisitor, + * but only for Clazz objects that are marked as kept. + * + * @see KeepMarker + * + * @author Eric Lafortune + */ +public class KeptClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new KeptClassFilter. + * @param classVisitor the class visitor to which the visiting will be + * delegated. + */ + public KeptClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (KeepMarker.isKept(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (KeepMarker.isKept(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/KeptMemberFilter.java b/src/proguard/optimize/KeptMemberFilter.java new file mode 100644 index 000000000..1bdadb4c5 --- /dev/null +++ b/src/proguard/optimize/KeptMemberFilter.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.MemberVisitor; + +/** + * This MemberVisitor delegates all its method calls to another MemberVisitor, + * but only for Member objects that are marked as kept. + * + * @see KeepMarker + * + * @author Eric Lafortune + */ +public class KeptMemberFilter +implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new KeptMemberFilter. + * @param memberVisitor the member visitor to which the visiting will be + * delegated. + */ + public KeptMemberFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (KeepMarker.isKept(programField)) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (KeepMarker.isKept(programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (KeepMarker.isKept(libraryField)) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (KeepMarker.isKept(libraryMethod)) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/MemberDescriptorSpecializer.java b/src/proguard/optimize/MemberDescriptorSpecializer.java new file mode 100644 index 000000000..4dce62e3f --- /dev/null +++ b/src/proguard/optimize/MemberDescriptorSpecializer.java @@ -0,0 +1,138 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 = false; + + + 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 000000000..d8d44250d --- /dev/null +++ b/src/proguard/optimize/MethodDescriptorShrinker.java @@ -0,0 +1,317 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.attribute.visitor.*; +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 extraMemberVisitor; + + + /** + * Creates a new MethodDescriptorShrinker. + */ + public MethodDescriptorShrinker() + { + this(null); + } + + + /** + * Creates a new MethodDescriptorShrinker with an extra visitor. + * @param extraMemberVisitor an optional extra visitor for all methods whose + * parameters have been simplified. + */ + public MethodDescriptorShrinker(MemberVisitor extraMemberVisitor) + { + this.extraMemberVisitor = extraMemberVisitor; + } + + + // 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(programMethod, descriptor); + + if (!descriptor.equals(newDescriptor)) + { + // Shrink the signature and parameter annotations. + programMethod.attributesAccept(programClass, this); + + String name = programMethod.getName(programClass); + String newName = name; + + // Append a code, if the method isn't a class instance initializer. + if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); + } + + if (DEBUG) + { + System.out.println("MethodDescriptorShrinker:"); + System.out.println(" ["+programClass.getName()+"."+ + name+descriptor+"] -> ["+ + newName+newDescriptor+"]"); + } + + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + // Update the name, if necessary. + if (!newName.equals(name)) + { + programMethod.u2nameIndex = + constantPoolEditor.addUtf8Constant(newName); + } + + // Update the referenced classes. + programMethod.referencedClasses = + shrinkReferencedClasses(programMethod, + descriptor, + programMethod.referencedClasses); + + // Finally, update the descriptor itself. + programMethod.u2descriptorIndex = + constantPoolEditor.addUtf8Constant(newDescriptor); + + // Visit the method, if required. + if (extraMemberVisitor != null) + { + extraMemberVisitor.visitProgramMethod(programClass, programMethod); + } + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + // Compute the new signature. + String signature = clazz.getString(signatureAttribute.u2signatureIndex); + String newSignature = shrinkDescriptor(method, signature); + + // Update the signature. + signatureAttribute.u2signatureIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); + + // Update the referenced classes. + signatureAttribute.referencedClasses = + shrinkReferencedClasses(method, + signature, + signatureAttribute.referencedClasses); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + int[] annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount; + 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 or signature of the given method. + */ + private String shrinkDescriptor(Method method, String descriptor) + { + // 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(descriptor); + + StringBuffer newDescriptorBuffer = new StringBuffer(); + + newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters()); + newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + 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 Clazz[] shrinkReferencedClasses(Method method, + String descriptor, + Clazz[] 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. + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(descriptor); + + // Also look at the formal type parameters. + String type = internalTypeEnumeration.formalTypeParameters(); + int count = new DescriptorClassEnumeration(type).classCount(); + for (int counter = 0; counter < count; counter++) + { + referencedClasses[newReferencedClassIndex++] = + referencedClasses[referencedClassIndex++]; + } + + while (internalTypeEnumeration.hasMoreTypes()) + { + // Consider the classes referenced by this parameter type. + type = internalTypeEnumeration.nextType(); + count = new DescriptorClassEnumeration(type).classCount(); + + if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) + { + // Copy the referenced classes. + for (int counter = 0; counter < count; counter++) + { + referencedClasses[newReferencedClassIndex++] = + referencedClasses[referencedClassIndex++]; + } + } + else + { + // Skip the referenced classes. + referencedClassIndex += count; + } + + parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; + } + + // Also look at the return value. + type = internalTypeEnumeration.returnType(); + count = new DescriptorClassEnumeration(type).classCount(); + for (int counter = 0; counter < count; counter++) + { + referencedClasses[newReferencedClassIndex++] = + referencedClasses[referencedClassIndex++]; + } + + // Clear the unused entries. + while (newReferencedClassIndex < referencedClassIndex) + { + referencedClasses[newReferencedClassIndex++] = null; + } + } + + return referencedClasses; + } +} diff --git a/src/proguard/optimize/MethodStaticizer.java b/src/proguard/optimize/MethodStaticizer.java new file mode 100644 index 000000000..c8bdd11dc --- /dev/null +++ b/src/proguard/optimize/MethodStaticizer.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/OptimizationInfoMemberFilter.java b/src/proguard/optimize/OptimizationInfoMemberFilter.java new file mode 100644 index 000000000..2c5454cab --- /dev/null +++ b/src/proguard/optimize/OptimizationInfoMemberFilter.java @@ -0,0 +1,94 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.MemberVisitor; +import proguard.optimize.info.*; + +/** + * This MemberVisitor delegates its visits to another given + * MemberVisitor, 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 MemberVisitor 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 new file mode 100644 index 000000000..8042825ec --- /dev/null +++ b/src/proguard/optimize/Optimizer.java @@ -0,0 +1,963 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize; + +import proguard.*; +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.visitor.*; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.visitor.*; +import proguard.classfile.util.MethodLinker; +import proguard.classfile.visitor.*; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.evaluation.*; +import proguard.optimize.info.*; +import proguard.optimize.peephole.*; +import proguard.util.*; + +import java.io.IOException; +import java.util.*; + +/** + * This class optimizes class pools according to a given configuration. + * + * @author Eric Lafortune + */ +public class Optimizer +{ + private static final String CLASS_MARKING_FINAL = "class/marking/final"; + private static final String CLASS_MERGING_VERTICAL = "class/merging/vertical"; + private static final String CLASS_MERGING_HORIZONTAL = "class/merging/horizontal"; + private static final String FIELD_REMOVAL_WRITEONLY = "field/removal/writeonly"; + private static final String FIELD_MARKING_PRIVATE = "field/marking/private"; + private static final String FIELD_PROPAGATION_VALUE = "field/propagation/value"; + private static final String METHOD_MARKING_PRIVATE = "method/marking/private"; + private static final String METHOD_MARKING_STATIC = "method/marking/static"; + private static final String METHOD_MARKING_FINAL = "method/marking/final"; + private static final String METHOD_REMOVAL_PARAMETER = "method/removal/parameter"; + private static final String METHOD_PROPAGATION_PARAMETER = "method/propagation/parameter"; + private static final String METHOD_PROPAGATION_RETURNVALUE = "method/propagation/returnvalue"; + private static final String METHOD_INLINING_SHORT = "method/inlining/short"; + private static final String METHOD_INLINING_UNIQUE = "method/inlining/unique"; + private static final String METHOD_INLINING_TAILRECURSION = "method/inlining/tailrecursion"; + private static final String CODE_MERGING = "code/merging"; + private static final String CODE_SIMPLIFICATION_VARIABLE = "code/simplification/variable"; + private static final String CODE_SIMPLIFICATION_ARITHMETIC = "code/simplification/arithmetic"; + private static final String CODE_SIMPLIFICATION_CAST = "code/simplification/cast"; + private static final String CODE_SIMPLIFICATION_FIELD = "code/simplification/field"; + private static final String CODE_SIMPLIFICATION_BRANCH = "code/simplification/branch"; + private static final String CODE_SIMPLIFICATION_STRING = "code/simplification/string"; + private static final String CODE_SIMPLIFICATION_ADVANCED = "code/simplification/advanced"; + private static final String CODE_REMOVAL_ADVANCED = "code/removal/advanced"; + private static final String CODE_REMOVAL_SIMPLE = "code/removal/simple"; + private static final String CODE_REMOVAL_VARIABLE = "code/removal/variable"; + private static final String CODE_REMOVAL_EXCEPTION = "code/removal/exception"; + private static final String CODE_ALLOCATION_VARIABLE = "code/allocation/variable"; + + + public static final String[] OPTIMIZATION_NAMES = new String[] + { + CLASS_MARKING_FINAL, + CLASS_MERGING_VERTICAL, + CLASS_MERGING_HORIZONTAL, + FIELD_REMOVAL_WRITEONLY, + FIELD_MARKING_PRIVATE, + FIELD_PROPAGATION_VALUE, + METHOD_MARKING_PRIVATE, + METHOD_MARKING_STATIC, + METHOD_MARKING_FINAL, + METHOD_REMOVAL_PARAMETER, + METHOD_PROPAGATION_PARAMETER, + METHOD_PROPAGATION_RETURNVALUE, + METHOD_INLINING_SHORT, + METHOD_INLINING_UNIQUE, + METHOD_INLINING_TAILRECURSION, + CODE_MERGING, + CODE_SIMPLIFICATION_VARIABLE, + CODE_SIMPLIFICATION_ARITHMETIC, + CODE_SIMPLIFICATION_CAST, + CODE_SIMPLIFICATION_FIELD, + CODE_SIMPLIFICATION_BRANCH, + CODE_SIMPLIFICATION_STRING, + CODE_SIMPLIFICATION_ADVANCED, + CODE_REMOVAL_ADVANCED, + CODE_REMOVAL_SIMPLE, + CODE_REMOVAL_VARIABLE, + CODE_REMOVAL_EXCEPTION, + CODE_ALLOCATION_VARIABLE, + }; + + + private final Configuration configuration; + + + /** + * Creates a new Optimizer. + */ + public Optimizer(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Performs optimization of the given program class pool. + */ + public boolean execute(ClassPool programClassPool, + ClassPool libraryClassPool) throws IOException + { + // Check if we have at least some keep commands. + if (configuration.keep == null && + configuration.applyMapping == null && + configuration.printMapping == null) + { + throw new IOException("You have to specify '-keep' options for the optimization step."); + } + + // Create a matcher for filtering optimizations. + StringMatcher filter = configuration.optimizations != null ? + new ListParser(new NameParser()).parse(configuration.optimizations) : + new ConstantMatcher(true); + + boolean classMarkingFinal = filter.matches(CLASS_MARKING_FINAL); + boolean classMergingVertical = filter.matches(CLASS_MERGING_VERTICAL); + boolean classMergingHorizontal = filter.matches(CLASS_MERGING_HORIZONTAL); + boolean fieldRemovalWriteonly = filter.matches(FIELD_REMOVAL_WRITEONLY); + boolean fieldMarkingPrivate = filter.matches(FIELD_MARKING_PRIVATE); + boolean fieldPropagationValue = filter.matches(FIELD_PROPAGATION_VALUE); + boolean methodMarkingPrivate = filter.matches(METHOD_MARKING_PRIVATE); + boolean methodMarkingStatic = filter.matches(METHOD_MARKING_STATIC); + boolean methodMarkingFinal = filter.matches(METHOD_MARKING_FINAL); + boolean methodRemovalParameter = filter.matches(METHOD_REMOVAL_PARAMETER); + boolean methodPropagationParameter = filter.matches(METHOD_PROPAGATION_PARAMETER); + boolean methodPropagationReturnvalue = filter.matches(METHOD_PROPAGATION_RETURNVALUE); + boolean methodInliningShort = filter.matches(METHOD_INLINING_SHORT); + boolean methodInliningUnique = filter.matches(METHOD_INLINING_UNIQUE); + boolean methodInliningTailrecursion = filter.matches(METHOD_INLINING_TAILRECURSION); + boolean codeMerging = filter.matches(CODE_MERGING); + boolean codeSimplificationVariable = filter.matches(CODE_SIMPLIFICATION_VARIABLE); + boolean codeSimplificationArithmetic = filter.matches(CODE_SIMPLIFICATION_ARITHMETIC); + boolean codeSimplificationCast = filter.matches(CODE_SIMPLIFICATION_CAST); + boolean codeSimplificationField = filter.matches(CODE_SIMPLIFICATION_FIELD); + boolean codeSimplificationBranch = filter.matches(CODE_SIMPLIFICATION_BRANCH); + boolean codeSimplificationString = filter.matches(CODE_SIMPLIFICATION_STRING); + boolean codeSimplificationAdvanced = filter.matches(CODE_SIMPLIFICATION_ADVANCED); + boolean codeRemovalAdvanced = filter.matches(CODE_REMOVAL_ADVANCED); + boolean codeRemovalSimple = filter.matches(CODE_REMOVAL_SIMPLE); + boolean codeRemovalVariable = filter.matches(CODE_REMOVAL_VARIABLE); + boolean codeRemovalException = filter.matches(CODE_REMOVAL_EXCEPTION); + boolean codeAllocationVariable = filter.matches(CODE_ALLOCATION_VARIABLE); + + // Create counters to count the numbers of optimizations. + ClassCounter classMarkingFinalCounter = new ClassCounter(); + ClassCounter classMergingVerticalCounter = new ClassCounter(); + ClassCounter classMergingHorizontalCounter = new ClassCounter(); + MemberCounter fieldRemovalWriteonlyCounter = new MemberCounter(); + MemberCounter fieldMarkingPrivateCounter = new MemberCounter(); + MemberCounter fieldPropagationValueCounter = new MemberCounter(); + MemberCounter methodMarkingPrivateCounter = new MemberCounter(); + MemberCounter methodMarkingStaticCounter = new MemberCounter(); + MemberCounter methodMarkingFinalCounter = new MemberCounter(); + MemberCounter methodRemovalParameterCounter = new MemberCounter(); + MemberCounter methodPropagationParameterCounter = new MemberCounter(); + MemberCounter methodPropagationReturnvalueCounter = new MemberCounter(); + InstructionCounter methodInliningShortCounter = new InstructionCounter(); + InstructionCounter methodInliningUniqueCounter = new InstructionCounter(); + InstructionCounter methodInliningTailrecursionCounter = new InstructionCounter(); + InstructionCounter codeMergingCounter = new InstructionCounter(); + InstructionCounter codeSimplificationVariableCounter = new InstructionCounter(); + InstructionCounter codeSimplificationArithmeticCounter = new InstructionCounter(); + InstructionCounter codeSimplificationCastCounter = new InstructionCounter(); + InstructionCounter codeSimplificationFieldCounter = new InstructionCounter(); + InstructionCounter codeSimplificationBranchCounter = new InstructionCounter(); + InstructionCounter codeSimplificationStringCounter = new InstructionCounter(); + InstructionCounter codeSimplificationAdvancedCounter = new InstructionCounter(); + InstructionCounter deletedCounter = new InstructionCounter(); + InstructionCounter addedCounter = new InstructionCounter(); + MemberCounter codeRemovalVariableCounter = new MemberCounter(); + ExceptionCounter codeRemovalExceptionCounter = new ExceptionCounter(); + MemberCounter codeAllocationVariableCounter = new MemberCounter(); + MemberCounter initializerFixCounter1 = new MemberCounter(); + MemberCounter initializerFixCounter2 = new MemberCounter(); + + // Some optimizations are required by other optimizations. + codeSimplificationAdvanced = + codeSimplificationAdvanced || + fieldPropagationValue || + methodPropagationParameter || + methodPropagationReturnvalue; + + codeRemovalAdvanced = + codeRemovalAdvanced || + fieldRemovalWriteonly || + methodMarkingStatic || + methodRemovalParameter; + + codeRemovalSimple = + codeRemovalSimple || + codeSimplificationBranch; + + codeRemovalException = + codeRemovalException || + codeRemovalAdvanced || + codeRemovalSimple; + + // Clean up any old visitor info. + programClassPool.classesAccept(new ClassCleaner()); + libraryClassPool.classesAccept(new ClassCleaner()); + + // Link all methods that should get the same optimization info. + 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 = + 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.classesAccept(keepMarker); + libraryClassPool.classesAccept(new AllMemberVisitor(keepMarker)); + + // We also keep all classes that are involved in .class constructs. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new DotClassClassVisitor(keepMarker))))); + + // We also keep all classes that are accessed dynamically. + programClassPool.classesAccept( + new AllConstantVisitor( + new ConstantTagFilter(ClassConstants.CONSTANT_String, + new ReferencedClassVisitor(keepMarker)))); + + // We also keep all class members that are accessed dynamically. + programClassPool.classesAccept( + new AllConstantVisitor( + new ConstantTagFilter(ClassConstants.CONSTANT_String, + new ReferencedMemberVisitor(keepMarker)))); + + // We also keep all bootstrap method signatures. + programClassPool.classesAccept( + new ClassVersionFilter(ClassConstants.INTERNAL_CLASS_VERSION_1_7, + new AllAttributeVisitor( + new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods, + new AllBootstrapMethodInfoVisitor( + new BootstrapMethodHandleTraveler( + new MethodrefTraveler( + new ReferencedMemberVisitor(keepMarker)))))))); + + // Attach some optimization info to all classes and class members, so + // it can be filled out later. + programClassPool.classesAccept(new ClassOptimizationInfoSetter()); + + programClassPool.classesAccept(new AllMemberVisitor( + new MemberOptimizationInfoSetter())); + + if (configuration.assumeNoSideEffects != null) + { + // Create a visitor for marking methods that don't have any side effects. + NoSideEffectMethodMarker noSideEffectMethodMarker = new NoSideEffectMethodMarker(); + ClassPoolVisitor noClassPoolvisitor = + ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.assumeNoSideEffects, + null, + noSideEffectMethodMarker); + + // Mark the seeds. + programClassPool.accept(noClassPoolvisitor); + libraryClassPool.accept(noClassPoolvisitor); + } + + if (classMarkingFinal) + { + // Make classes final, whereever possible. + programClassPool.classesAccept( + new ClassFinalizer(classMarkingFinalCounter)); + } + + if (methodMarkingFinal) + { + // Make methods final, whereever possible. + programClassPool.classesAccept( + new AllMethodVisitor( + new MethodFinalizer(methodMarkingFinalCounter))); + } + + if (fieldRemovalWriteonly) + { + // Mark all fields that are write-only. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new ReadWriteFieldMarker())))); + + // Count the write-only fields. + programClassPool.classesAccept( + new AllFieldVisitor( + new WriteOnlyFieldFilter(fieldRemovalWriteonlyCounter))); + } + else + { + // Mark all fields as read/write. + programClassPool.classesAccept( + new AllFieldVisitor( + new ReadWriteFieldMarker())); + } + + // Mark all used parameters, including the 'this' parameters. + programClassPool.classesAccept( + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + new ParameterUsageMarker(!methodMarkingStatic, + !methodRemovalParameter)))); + + // Mark all classes that have static initializers. + programClassPool.classesAccept(new StaticInitializerContainingClassMarker()); + + // Mark all methods that have side effects. + programClassPool.accept(new SideEffectMethodMarker()); + +// System.out.println("Optimizer.execute: before evaluation simplification"); +// programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new ClassPrinter())); + + // Perform partial evaluation for filling out fields, method parameters, + // and method return values. + ValueFactory valueFactory = new IdentifiedValueFactory(); + + if (fieldPropagationValue || + methodPropagationParameter || + methodPropagationReturnvalue) + { + // Fill out fields, method parameters, and return values, so they + // can be propagated. + InvocationUnit storingInvocationUnit = + new StoringInvocationUnit(valueFactory, + fieldPropagationValue, + methodPropagationParameter, + methodPropagationReturnvalue); + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new PartialEvaluator(valueFactory, storingInvocationUnit, false)))); + + if (fieldPropagationValue) + { + // Count the constant fields. + programClassPool.classesAccept( + new AllFieldVisitor( + new ConstantMemberFilter(fieldPropagationValueCounter))); + } + + if (methodPropagationParameter) + { + // Count the constant method parameters. + programClassPool.classesAccept( + new AllMethodVisitor( + new ConstantParameterFilter(methodPropagationParameterCounter))); + } + + if (methodPropagationReturnvalue) + { + // Count the constant method return values. + programClassPool.classesAccept( + new AllMethodVisitor( + new ConstantMemberFilter(methodPropagationReturnvalueCounter))); + } + } + + InvocationUnit loadingInvocationUnit = + new LoadingInvocationUnit(valueFactory, + fieldPropagationValue, + methodPropagationParameter, + methodPropagationReturnvalue); + + if (codeSimplificationAdvanced) + { + // Simplify based on partial evaluation, propagating constant + // field values, method parameter values, and return values. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new EvaluationSimplifier( + new PartialEvaluator(valueFactory, loadingInvocationUnit, false), + codeSimplificationAdvancedCounter)))); + } + + if (codeRemovalAdvanced) + { + // Remove code based on partial evaluation, also removing unused + // parameters from method invocations, and making methods static + // if possible. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new EvaluationShrinker( + new PartialEvaluator(valueFactory, loadingInvocationUnit, !codeSimplificationAdvanced), + deletedCounter, addedCounter)))); + } + + if (methodRemovalParameter) + { + // Shrink the parameters in the method descriptors. + programClassPool.classesAccept( + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + new MethodDescriptorShrinker()))); + } + + if (methodMarkingStatic) + { + // Make all non-static methods that don't require the 'this' + // parameter static. + programClassPool.classesAccept( + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC, + new MethodStaticizer(methodMarkingStaticCounter))))); + } + + if (methodRemovalParameter) + { + // Fix all references to class members. + // This operation also updates the stack sizes. + programClassPool.classesAccept( + new MemberReferenceFixer()); + + // Remove unused bootstrap method arguments. + programClassPool.classesAccept( + new AllAttributeVisitor( + new AllBootstrapMethodInfoVisitor( + new BootstrapMethodArgumentShrinker()))); + } + + if (methodRemovalParameter || + methodMarkingPrivate || + methodMarkingStatic) + { + // Remove all unused parameters from the byte code, shifting all + // remaining variables. + // This operation also updates the local variable frame sizes. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new ParameterShrinker(methodRemovalParameterCounter)))); + } + else if (codeRemovalAdvanced) + { + // Just update the local variable frame sizes. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new StackSizeUpdater()))); + } + + if (methodRemovalParameter && + methodRemovalParameterCounter.getCount() > 0) + { + // Tweak the descriptors of duplicate initializers, due to removed + // method parameters. + programClassPool.classesAccept( + new AllMethodVisitor( + new DuplicateInitializerFixer(initializerFixCounter1))); + + if (initializerFixCounter1.getCount() > 0) + { + // Fix all invocations of tweaked initializers. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new DuplicateInitializerInvocationFixer(addedCounter)))); + + // Fix all references to tweaked initializers. + programClassPool.classesAccept(new MemberReferenceFixer()); + } + } + + //// 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)))); + + // Mark all classes with package visible members. + // Mark all exception catches of methods. + // Count all method invocations. + // Mark super invocations and other access of methods. + programClassPool.classesAccept( + new MultiClassVisitor( + new ClassVisitor[] + { + new PackageVisibleMemberContainingClassMarker(), + new AllConstantVisitor( + new PackageVisibleMemberInvokingClassMarker()), + new AllMethodVisitor( + new MultiMemberVisitor( + new MemberVisitor[] + { + new AllAttributeVisitor( + new MultiAttributeVisitor( + new AttributeVisitor[] + { + new CatchExceptionMarker(), + new AllInstructionVisitor( + new MultiInstructionVisitor( + new InstructionVisitor[] + { + new InstantiationClassMarker(), + new InstanceofClassMarker(), + new DotClassMarker(), + new MethodInvocationMarker(), + new SuperInvocationMarker(), + new BackwardBranchMarker(), + new AccessMethodMarker(), + })), + new AllExceptionInfoVisitor( + new ExceptionHandlerConstantVisitor( + new ReferencedClassVisitor( + new CaughtClassMarker()))), + })), + })), + })); + + if (classMergingVertical) + { + // Merge classes into their superclasses or interfaces. + programClassPool.classesAccept( + new VerticalClassMerger(configuration.allowAccessModification, + configuration.mergeInterfacesAggressively, + classMergingVerticalCounter)); + } + + if (classMergingHorizontal) + { + // Merge classes into their sibling classes. + programClassPool.classesAccept( + new HorizontalClassMerger(configuration.allowAccessModification, + configuration.mergeInterfacesAggressively, + classMergingHorizontalCounter)); + } + + if (classMergingVerticalCounter .getCount() > 0 || + classMergingHorizontalCounter.getCount() > 0) + { + // Clean up inner class attributes to avoid loops. + programClassPool.classesAccept(new RetargetedInnerClassAttributeRemover()); + + // Update references to merged classes. + programClassPool.classesAccept(new TargetClassChanger()); + programClassPool.classesAccept(new ClassReferenceFixer(true)); + programClassPool.classesAccept(new MemberReferenceFixer()); + + if (configuration.allowAccessModification) + { + // Fix the access flags of referenced merged classes and their + // class members. + programClassPool.classesAccept( + new AllConstantVisitor( + new AccessFixer())); + } + + // Fix the access flags of the inner classes information. + programClassPool.classesAccept( + new AllAttributeVisitor( + new AllInnerClassesInfoVisitor( + new InnerClassesAccessFixer()))); + + // Tweak the descriptors of duplicate initializers, due to merged + // parameter classes. + programClassPool.classesAccept( + new AllMethodVisitor( + new DuplicateInitializerFixer(initializerFixCounter2))); + + if (initializerFixCounter2.getCount() > 0) + { + // Fix all invocations of tweaked initializers. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new DuplicateInitializerInvocationFixer(addedCounter)))); + + // Fix all references to tweaked initializers. + programClassPool.classesAccept(new MemberReferenceFixer()); + } + } + + if (methodInliningUnique) + { + // Inline methods that are only invoked once. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new MethodInliner(configuration.microEdition, + configuration.allowAccessModification, + true, + methodInliningUniqueCounter)))); + } + + if (methodInliningShort) + { + // Inline short methods. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new MethodInliner(configuration.microEdition, + configuration.allowAccessModification, + false, + methodInliningShortCounter)))); + } + + if (methodInliningTailrecursion) + { + // Simplify tail recursion calls. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new TailRecursionSimplifier(methodInliningTailrecursionCounter)))); + } + + if (fieldMarkingPrivate || + methodMarkingPrivate) + { + // Mark all class members that can not be made private. + programClassPool.classesAccept( + new NonPrivateMemberMarker()); + } + + if (fieldMarkingPrivate) + { + // Make all non-private fields private, whereever possible. + programClassPool.classesAccept( + new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE, + new AllFieldVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberPrivatizer(fieldMarkingPrivateCounter))))); + } + + if (methodMarkingPrivate) + { + // Make all non-private methods private, whereever possible. + programClassPool.classesAccept( + new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE, + new AllMethodVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberPrivatizer(methodMarkingPrivateCounter))))); + } + + if ((methodInliningUniqueCounter .getCount() > 0 || + methodInliningShortCounter .getCount() > 0 || + methodInliningTailrecursionCounter.getCount() > 0) && + configuration.allowAccessModification) + { + // Fix the access flags of referenced classes and class members, + // for MethodInliner. + programClassPool.classesAccept( + new AllConstantVisitor( + new AccessFixer())); + } + + if (methodRemovalParameterCounter .getCount() > 0 || + classMergingVerticalCounter .getCount() > 0 || + classMergingHorizontalCounter .getCount() > 0 || + methodMarkingPrivateCounter .getCount() > 0 ) + { + // Fix invocations of interface methods, of methods that have become + // non-abstract or private, and of methods that have moved to a + // different package. + programClassPool.classesAccept( + new AllMemberVisitor( + new AllAttributeVisitor( + new MethodInvocationFixer()))); + } + + if (codeMerging) + { + // Share common blocks of code at branches. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new GotoCommonCodeReplacer(codeMergingCounter)))); + } + + // Create a branch target marker and a code attribute editor that can + // be reused for all code attributes. + BranchTargetFinder branchTargetFinder = new BranchTargetFinder(); + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + List peepholeOptimizations = new ArrayList(); + if (codeSimplificationVariable) + { + // Peephole optimizations involving local variables. + peepholeOptimizations.add( + new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS, + InstructionSequenceConstants.VARIABLE, + branchTargetFinder, codeAttributeEditor, codeSimplificationVariableCounter)); + } + + if (codeSimplificationArithmetic) + { + // Peephole optimizations involving arithmetic operations. + peepholeOptimizations.add( + new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS, + InstructionSequenceConstants.ARITHMETIC, + branchTargetFinder, codeAttributeEditor, codeSimplificationArithmeticCounter)); + } + + if (codeSimplificationCast) + { + // Peephole optimizations involving cast operations. + peepholeOptimizations.add( + new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS, + InstructionSequenceConstants.CAST, + branchTargetFinder, codeAttributeEditor, codeSimplificationCastCounter)); + } + + if (codeSimplificationField) + { + // Peephole optimizations involving fields. + peepholeOptimizations.add( + new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS, + InstructionSequenceConstants.FIELD, + branchTargetFinder, codeAttributeEditor, codeSimplificationFieldCounter)); + } + + if (codeSimplificationBranch) + { + // Peephole optimizations involving branches. + peepholeOptimizations.add( + new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS, + InstructionSequenceConstants.BRANCH, + branchTargetFinder, codeAttributeEditor, codeSimplificationBranchCounter)); + + // Include optimization of branches to branches and returns. + peepholeOptimizations.add( + new GotoGotoReplacer(codeAttributeEditor, codeSimplificationBranchCounter)); + peepholeOptimizations.add( + new GotoReturnReplacer(codeAttributeEditor, codeSimplificationBranchCounter)); + } + + if (codeSimplificationString) + { + // Peephole optimizations involving branches. + peepholeOptimizations.add( + new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS, + InstructionSequenceConstants.STRING, + branchTargetFinder, codeAttributeEditor, codeSimplificationStringCounter)); + } + + if (!peepholeOptimizations.isEmpty()) + { + // Convert the list into an array. + InstructionVisitor[] peepholeOptimizationsArray = + new InstructionVisitor[peepholeOptimizations.size()]; + peepholeOptimizations.toArray(peepholeOptimizationsArray); + + // Perform the peephole optimisations. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new PeepholeOptimizer(branchTargetFinder, codeAttributeEditor, + new MultiInstructionVisitor( + peepholeOptimizationsArray))))); + } + + if (codeRemovalException) + { + // Remove unnecessary exception handlers. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new UnreachableExceptionRemover(codeRemovalExceptionCounter)))); + } + + if (codeRemovalSimple) + { + // Remove unreachable code. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new UnreachableCodeRemover(deletedCounter)))); + } + + if (codeRemovalVariable) + { + // Remove all unused local variables. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new VariableShrinker(codeRemovalVariableCounter)))); + } + + if (codeAllocationVariable) + { + // Optimize the variables. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new VariableOptimizer(false, codeAllocationVariableCounter)))); + } + + + // Remove unused constants. + programClassPool.classesAccept( + new ConstantPoolShrinker()); + + int classMarkingFinalCount = classMarkingFinalCounter .getCount(); + int classMergingVerticalCount = classMergingVerticalCounter .getCount(); + int classMergingHorizontalCount = classMergingHorizontalCounter .getCount(); + int fieldRemovalWriteonlyCount = fieldRemovalWriteonlyCounter .getCount(); + int fieldMarkingPrivateCount = fieldMarkingPrivateCounter .getCount(); + int fieldPropagationValueCount = fieldPropagationValueCounter .getCount(); + int methodMarkingPrivateCount = methodMarkingPrivateCounter .getCount(); + int methodMarkingStaticCount = methodMarkingStaticCounter .getCount(); + int methodMarkingFinalCount = methodMarkingFinalCounter .getCount(); + int methodRemovalParameterCount = methodRemovalParameterCounter .getCount() - methodMarkingStaticCounter.getCount() - initializerFixCounter1.getCount() - initializerFixCounter2.getCount(); + int methodPropagationParameterCount = methodPropagationParameterCounter .getCount(); + int methodPropagationReturnvalueCount = methodPropagationReturnvalueCounter.getCount(); + int methodInliningShortCount = methodInliningShortCounter .getCount(); + int methodInliningUniqueCount = methodInliningUniqueCounter .getCount(); + int methodInliningTailrecursionCount = methodInliningTailrecursionCounter .getCount(); + int codeMergingCount = codeMergingCounter .getCount(); + int codeSimplificationVariableCount = codeSimplificationVariableCounter .getCount(); + int codeSimplificationArithmeticCount = codeSimplificationArithmeticCounter.getCount(); + int codeSimplificationCastCount = codeSimplificationCastCounter .getCount(); + int codeSimplificationFieldCount = codeSimplificationFieldCounter .getCount(); + int codeSimplificationBranchCount = codeSimplificationBranchCounter .getCount(); + int codeSimplificationStringCount = codeSimplificationStringCounter .getCount(); + int codeSimplificationAdvancedCount = codeSimplificationAdvancedCounter .getCount(); + int codeRemovalCount = deletedCounter .getCount() - addedCounter.getCount(); + int codeRemovalVariableCount = codeRemovalVariableCounter .getCount(); + int codeRemovalExceptionCount = codeRemovalExceptionCounter .getCount(); + int codeAllocationVariableCount = codeAllocationVariableCounter .getCount(); + + // Forget about constant fields, parameters, and return values, if they + // didn't lead to any useful optimizations. We want to avoid fruitless + // additional optimization passes. + if (codeSimplificationAdvancedCount == 0) + { + fieldPropagationValueCount = 0; + methodPropagationParameterCount = 0; + methodPropagationReturnvalueCount = 0; + } + + if (configuration.verbose) + { + System.out.println(" Number of finalized classes: " + classMarkingFinalCount + disabled(classMarkingFinal)); + System.out.println(" Number of vertically merged classes: " + classMergingVerticalCount + disabled(classMergingVertical)); + System.out.println(" Number of horizontally merged classes: " + classMergingHorizontalCount + disabled(classMergingHorizontal)); + System.out.println(" Number of removed write-only fields: " + fieldRemovalWriteonlyCount + disabled(fieldRemovalWriteonly)); + System.out.println(" Number of privatized fields: " + fieldMarkingPrivateCount + disabled(fieldMarkingPrivate)); + System.out.println(" Number of inlined constant fields: " + fieldPropagationValueCount + disabled(fieldPropagationValue)); + System.out.println(" Number of privatized methods: " + methodMarkingPrivateCount + disabled(methodMarkingPrivate)); + System.out.println(" Number of staticized methods: " + methodMarkingStaticCount + disabled(methodMarkingStatic)); + System.out.println(" Number of finalized methods: " + methodMarkingFinalCount + disabled(methodMarkingFinal)); + System.out.println(" Number of removed method parameters: " + methodRemovalParameterCount + disabled(methodRemovalParameter)); + System.out.println(" Number of inlined constant parameters: " + methodPropagationParameterCount + disabled(methodPropagationParameter)); + System.out.println(" Number of inlined constant return values: " + methodPropagationReturnvalueCount + disabled(methodPropagationReturnvalue)); + System.out.println(" Number of inlined short method calls: " + methodInliningShortCount + disabled(methodInliningShort)); + System.out.println(" Number of inlined unique method calls: " + methodInliningUniqueCount + disabled(methodInliningUnique)); + System.out.println(" Number of inlined tail recursion calls: " + methodInliningTailrecursionCount + disabled(methodInliningTailrecursion)); + System.out.println(" Number of merged code blocks: " + codeMergingCount + disabled(codeMerging)); + System.out.println(" Number of variable peephole optimizations: " + codeSimplificationVariableCount + disabled(codeSimplificationVariable)); + System.out.println(" Number of arithmetic peephole optimizations: " + codeSimplificationArithmeticCount + disabled(codeSimplificationArithmetic)); + System.out.println(" Number of cast peephole optimizations: " + codeSimplificationCastCount + disabled(codeSimplificationCast)); + System.out.println(" Number of field peephole optimizations: " + codeSimplificationFieldCount + disabled(codeSimplificationField)); + System.out.println(" Number of branch peephole optimizations: " + codeSimplificationBranchCount + disabled(codeSimplificationBranch)); + System.out.println(" Number of string peephole optimizations: " + codeSimplificationStringCount + disabled(codeSimplificationString)); + System.out.println(" Number of simplified instructions: " + codeSimplificationAdvancedCount + disabled(codeSimplificationAdvanced)); + System.out.println(" Number of removed instructions: " + codeRemovalCount + disabled(codeRemovalAdvanced)); + System.out.println(" Number of removed local variables: " + codeRemovalVariableCount + disabled(codeRemovalVariable)); + System.out.println(" Number of removed exception blocks: " + codeRemovalExceptionCount + disabled(codeRemovalException)); + System.out.println(" Number of optimized local variable frames: " + codeAllocationVariableCount + disabled(codeAllocationVariable)); + } + + return classMarkingFinalCount > 0 || + classMergingVerticalCount > 0 || + classMergingHorizontalCount > 0 || + fieldRemovalWriteonlyCount > 0 || + fieldMarkingPrivateCount > 0 || + methodMarkingPrivateCount > 0 || + methodMarkingStaticCount > 0 || + methodMarkingFinalCount > 0 || + fieldPropagationValueCount > 0 || + methodRemovalParameterCount > 0 || + methodPropagationParameterCount > 0 || + methodPropagationReturnvalueCount > 0 || + methodInliningShortCount > 0 || + methodInliningUniqueCount > 0 || + methodInliningTailrecursionCount > 0 || + codeMergingCount > 0 || + codeSimplificationVariableCount > 0 || + codeSimplificationArithmeticCount > 0 || + codeSimplificationCastCount > 0 || + codeSimplificationFieldCount > 0 || + codeSimplificationBranchCount > 0 || + codeSimplificationStringCount > 0 || + codeSimplificationAdvancedCount > 0 || + codeRemovalCount > 0 || + codeRemovalVariableCount > 0 || + codeRemovalExceptionCount > 0 || + codeAllocationVariableCount > 0; + } + + + /** + * Returns a String indicating whether the given flag is enabled or + * disabled. + */ + private String disabled(boolean flag) + { + return flag ? "" : " (disabled)"; + } + + + /** + * Returns a String indicating whether the given flags are enabled or + * disabled. + */ + private String disabled(boolean flag1, boolean flag2) + { + return flag1 && flag2 ? "" : + flag1 || flag2 ? " (partially disabled)" : + " (disabled)"; + } +} diff --git a/src/proguard/optimize/ParameterShrinker.java b/src/proguard/optimize/ParameterShrinker.java new file mode 100644 index 000000000..33d37d141 --- /dev/null +++ b/src/proguard/optimize/ParameterShrinker.java @@ -0,0 +1,146 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.editor.VariableRemapper; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; +import proguard.optimize.info.ParameterUsageMarker; + +/** + * 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 +extends SimplifiedVisitor +implements AttributeVisitor +{ + private static final boolean DEBUG = false; + + + private final MemberVisitor extraVariableMemberVisitor; + + private final VariableRemapper variableRemapper = new VariableRemapper(); + + + /** + * Creates a new ParameterShrinker. + */ + public ParameterShrinker() + { + this(null); + } + + + /** + * Creates a new ParameterShrinker with an extra visitor. + * @param extraVariableMemberVisitor an optional extra visitor for all + * removed parameters. + */ + public ParameterShrinker(MemberVisitor extraVariableMemberVisitor) + { + this.extraVariableMemberVisitor = extraVariableMemberVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Get the original parameter size that was saved. + int oldParameterSize = ParameterUsageMarker.getParameterSize(method); + + // Compute the new parameter size from the shrunk descriptor. + int newParameterSize = + ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), + method.getAccessFlags()); + + if (oldParameterSize > newParameterSize) + { + // Get the total size of the local variable frame. + int maxLocals = codeAttribute.u2maxLocals; + + if (DEBUG) + { + System.out.println("ParameterShrinker: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + System.out.println(" Old parameter size = " + oldParameterSize); + System.out.println(" New parameter size = " + newParameterSize); + System.out.println(" Max locals = " + maxLocals); + } + + // Create a variable map. + int[] variableMap = new int[maxLocals]; + + // Move unused parameters right after the parameter block. + int usedParameterIndex = 0; + int unusedParameterIndex = newParameterSize; + for (int parameterIndex = 0; parameterIndex < oldParameterSize; parameterIndex++) + { + // Is the variable required as a parameter? + if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) + { + // Keep the variable as a parameter. + variableMap[parameterIndex] = usedParameterIndex++; + } + else + { + if (DEBUG) + { + System.out.println(" Deleting parameter #"+parameterIndex); + } + + // Shift the variable to the unused parameter block, + // in case it is still used as a variable. + variableMap[parameterIndex] = unusedParameterIndex++; + + // Visit the method, if required. + if (extraVariableMemberVisitor != null) + { + method.accept(clazz, extraVariableMemberVisitor); + } + } + } + + // Fill out the remainder of the map. + for (int variableIndex = oldParameterSize; variableIndex < maxLocals; variableIndex++) + { + variableMap[variableIndex] = variableIndex; + } + + // Set the map. + variableRemapper.setVariableMap(variableMap); + + // Remap the variables. + variableRemapper.visitCodeAttribute(clazz, method, codeAttribute); + } + } +} diff --git a/src/proguard/optimize/TailRecursionSimplifier.java b/src/proguard/optimize/TailRecursionSimplifier.java new file mode 100644 index 000000000..f820566a9 --- /dev/null +++ b/src/proguard/optimize/TailRecursionSimplifier.java @@ -0,0 +1,356 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.MethodrefConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.CodeAttributeComposer; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; + +/** + * This MemberVisitor simplifies tail recursion calls in all methods that it + * visits. + * + * @author Eric Lafortune + */ +public class TailRecursionSimplifier +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + ExceptionInfoVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + + private final InstructionVisitor extraTailRecursionVisitor; + + + private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); + private final MyRecursionChecker recursionChecker = new MyRecursionChecker(); + + private Method targetMethod; + private boolean inlinedAny; + + + + /** + * Creates a new TailRecursionSimplifier. + */ + public TailRecursionSimplifier() + { + this(null); + } + + + /** + * Creates a new TailRecursionSimplifier with an extra visitor. + * @param extraTailRecursionVisitor an optional extra visitor for all + * simplified tail recursions. + */ + public TailRecursionSimplifier(InstructionVisitor extraTailRecursionVisitor) + { + this.extraTailRecursionVisitor = extraTailRecursionVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + int accessFlags = method.getAccessFlags(); + + if (// Only check the method if it is private, static, or final. + (accessFlags & (ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC | + ClassConstants.INTERNAL_ACC_FINAL)) != 0 && + + // Only check 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) + { +// codeAttributeComposer.DEBUG = DEBUG = +// clazz.getName().equals("abc/Def") && +// method.getName(clazz).equals("abc"); + + targetMethod = method; + inlinedAny = false; + codeAttributeComposer.reset(); + + // The code may expand, due to expanding constant and variable + // instructions. + codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); + + // Copy the instructions. + codeAttribute.instructionsAccept(clazz, method, this); + + // Update the code attribute if any code has been inlined. + if (inlinedAny) + { + // Copy the exceptions. + codeAttribute.exceptionsAccept(clazz, method, this); + + // Append a label just after the code. + codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); + + codeAttributeComposer.endCodeFragment(); + + codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); + } + } + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Copy the instruction. + codeAttributeComposer.appendInstruction(offset, instruction); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Is it a method invocation? + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + { + // Is it a recursive call? + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, recursionChecker); + + if (recursionChecker.isRecursive()) + { + // Is the next instruction a return? + int nextOffset = + offset + constantInstruction.length(offset); + + Instruction nextInstruction = + InstructionFactory.create(codeAttribute.code, nextOffset); + + switch (nextInstruction.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: + { + // Isn't the recursive call inside a try/catch block? + codeAttribute.exceptionsAccept(clazz, method, offset, recursionChecker); + + if (recursionChecker.isRecursive()) + { + if (DEBUG) + { + System.out.println("TailRecursionSimplifier: ["+ + clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"], inlining "+constantInstruction.toString(offset)); + } + + // Append a label. + codeAttributeComposer.appendLabel(offset); + + storeParameters(clazz, method); + + // Branch back to the start of the method. + int gotoOffset = offset + 1; + codeAttributeComposer.appendInstruction(gotoOffset, + new BranchInstruction(InstructionConstants.OP_GOTO, -gotoOffset)); + + // The original return instruction will be + // removed elsewhere, if possible. + + // Remember that the code has changed. + inlinedAny = true; + + if (extraTailRecursionVisitor != null) + { + extraTailRecursionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } + + // The invocation itself is no longer necessary. + return; + } + } + } + } + + break; + } + } + + // Copy the instruction. + codeAttributeComposer.appendInstruction(offset, constantInstruction); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + codeAttributeComposer.appendException(new ExceptionInfo(exceptionInfo.u2startPC, + exceptionInfo.u2endPC, + exceptionInfo.u2handlerPC, + exceptionInfo.u2catchType)); + } + + + /** + * This ConstantVisitor and ExceptionInfoVisitor returns whether a method + * invocation can be treated as tail-recursive. + */ + private class MyRecursionChecker + extends SimplifiedVisitor + implements ConstantVisitor, + ExceptionInfoVisitor + { + private boolean recursive; + + + /** + * Returns whether the method invocation can be treated as + * tail-recursive. + */ + public boolean isRecursive() + { + return recursive; + } + + // Implementations for ConstantVisitor. + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + recursive = targetMethod.equals(methodrefConstant.referencedMember); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + recursive = false; + } + } + + + // Small utility methods. + + /** + * 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 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(parameterSize + 1); + + // 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, parameterOffset + parameterIndex)); + } + } + + // Put the 'this' reference in variable 0. + if (!isStatic) + { + codeAttributeComposer.appendInstruction(parameterSize, + new VariableInstruction(InstructionConstants.OP_ASTORE, 0)); + } + + codeAttributeComposer.endCodeFragment(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/WriteOnlyFieldFilter.java b/src/proguard/optimize/WriteOnlyFieldFilter.java new file mode 100644 index 000000000..762bd91d2 --- /dev/null +++ b/src/proguard/optimize/WriteOnlyFieldFilter.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; +import proguard.classfile.visitor.MemberVisitor; +import proguard.optimize.info.ReadWriteFieldMarker; + +/** + * This MemberVisitor delegates its visits to program fields to + * other given MemberVisitor instances, but only when the visited + * field has been marked as write-only. + * + * @see ReadWriteFieldMarker + * @author Eric Lafortune + */ +public class WriteOnlyFieldFilter +extends SimplifiedVisitor +implements MemberVisitor +{ + private final MemberVisitor writeOnlyFieldVisitor; + + + /** + * Creates a new WriteOnlyFieldFilter. + * @param writeOnlyFieldVisitor the MemberVisitor to which + * visits to write-only fields will be delegated. + */ + public WriteOnlyFieldFilter(MemberVisitor writeOnlyFieldVisitor) + { + this.writeOnlyFieldVisitor = writeOnlyFieldVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + + if (ReadWriteFieldMarker.isWritten(programField) && + !ReadWriteFieldMarker.isRead(programField)) + { + writeOnlyFieldVisitor.visitProgramField(programClass, programField); + } + } +} diff --git a/src/proguard/optimize/evaluation/EvaluationShrinker.java b/src/proguard/optimize/evaluation/EvaluationShrinker.java new file mode 100644 index 000000000..2e86532a8 --- /dev/null +++ b/src/proguard/optimize/evaluation/EvaluationShrinker.java @@ -0,0 +1,2222 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.RefConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.CodeAttributeEditor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.info.*; + +import java.util.Arrays; + +/** + * This AttributeVisitor simplifies the code attributes that it visits, based + * on partial evaluation. + * + * @author Eric Lafortune + */ +public class EvaluationShrinker +extends SimplifiedVisitor +implements AttributeVisitor +{ + //* + private static final boolean DEBUG_RESULTS = false; + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG_RESULTS = true; + private static boolean DEBUG = true; + //*/ + + private static final int UNSUPPORTED = -1; + private static final int NOP = InstructionConstants.OP_NOP & 0xff; + private static final int POP = InstructionConstants.OP_POP & 0xff; + private static final int POP2 = InstructionConstants.OP_POP2 & 0xff; + private static final int DUP = InstructionConstants.OP_DUP & 0xff; + private static final int DUP_X1 = InstructionConstants.OP_DUP_X1 & 0xff; + private static final int DUP_X2 = InstructionConstants.OP_DUP_X2 & 0xff; + private static final int DUP2 = InstructionConstants.OP_DUP2 & 0xff; + private static final int DUP2_X1 = InstructionConstants.OP_DUP2_X1 & 0xff; + private static final int DUP2_X2 = InstructionConstants.OP_DUP2_X2 & 0xff; + private static final int SWAP = InstructionConstants.OP_SWAP & 0xff; + private static final int MOV_X2 = DUP_X2 | (POP << 8); + private static final int MOV2_X1 = DUP2_X1 | (POP2 << 8); + private static final int MOV2_X2 = DUP2_X2 | (POP2 << 8); + private static final int POP_X1 = SWAP | (POP << 8); + private static final int POP_X2 = DUP2_X1 | (POP2 << 8) | (POP << 16); + private static final int POP_X3 = UNSUPPORTED; + private static final int POP2_X1 = DUP_X2 | (POP << 8) | (POP2 << 16); + private static final int POP2_X2 = DUP2_X2 | (POP2 << 8) | (POP2 << 16); + private static final int POP3 = POP2 | (POP << 8); + private static final int POP4 = POP2 | (POP2 << 8); + private static final int POP_DUP = POP | (DUP << 8); + private static final int POP_SWAP_POP = POP | (SWAP << 8) | (POP << 16); + private static final int POP2_SWAP_POP = POP2 | (SWAP << 8) | (POP << 16); + private static final int SWAP_DUP_X1 = SWAP | (DUP_X1 << 8); + private static final int SWAP_DUP_X1_SWAP = SWAP | (DUP_X1 << 8) | (SWAP << 16); + private static final int SWAP_POP_DUP = SWAP | (POP << 8) | (DUP << 16); + private static final int SWAP_POP_DUP_X1 = SWAP | (POP << 8) | (DUP_X1 << 16); + private static final int DUP_X2_POP2 = DUP_X2 | (POP2 << 8); + private static final int DUP2_X1_POP3 = DUP2_X1 | (POP2 << 8) | (POP << 16); + private static final int DUP2_X2_POP3 = DUP2_X2 | (POP2 << 8) | (POP << 16); + private static final int DUP2_X2_SWAP_POP = DUP2_X2 | (SWAP << 8) | (POP << 16); + + + private final InstructionVisitor extraDeletedInstructionVisitor; + private final InstructionVisitor extraAddedInstructionVisitor; + + private final PartialEvaluator partialEvaluator; + private final PartialEvaluator simplePartialEvaluator = new PartialEvaluator(); + private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); + private final MyUnusedParameterSimplifier unusedParameterSimplifier = new MyUnusedParameterSimplifier(); + private final MyProducerMarker producerMarker = new MyProducerMarker(); + private final MyVariableInitializationMarker variableInitializationMarker = new MyVariableInitializationMarker(); + private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer(); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, false); + + private boolean[][] stacksNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; + private boolean[][] stacksSimplifiedBefore = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; + private boolean[] instructionsNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + + private int maxMarkedOffset; + + + /** + * Creates a new EvaluationShrinker. + */ + public EvaluationShrinker() + { + this(new PartialEvaluator(), null, null); + } + + + /** + * Creates a new EvaluationShrinker. + * @param partialEvaluator the partial evaluator that will + * execute the code and provide + * information about the results. + * @param extraDeletedInstructionVisitor an optional extra visitor for all + * deleted instructions. + * @param extraAddedInstructionVisitor an optional extra visitor for all + * added instructions. + */ + public EvaluationShrinker(PartialEvaluator partialEvaluator, + InstructionVisitor extraDeletedInstructionVisitor, + InstructionVisitor extraAddedInstructionVisitor) + { + this.partialEvaluator = partialEvaluator; + this.extraDeletedInstructionVisitor = extraDeletedInstructionVisitor; + this.extraAddedInstructionVisitor = extraAddedInstructionVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { +// DEBUG = DEBUG_RESULTS = +// clazz.getName().equals("abc/Def") && +// method.getName(clazz).equals("abc"); + + // TODO: Remove this when the evaluation shrinker 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 shrinking instructions after partial evaluation:"); + System.err.println(" Class = ["+clazz.getName()+"]"); + System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + System.err.println("Not optimizing this method"); + + if (DEBUG) + { + method.accept(clazz, new ClassPrinter()); + + throw ex; + } + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG_RESULTS) + { + System.out.println(); + System.out.println("Class "+ClassUtil.externalClassName(clazz.getName())); + System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(), + 0, + method.getName(clazz), + method.getDescriptor(clazz))); + } + + // Initialize the necessary array. + initializeNecessary(codeAttribute); + + // Evaluate the method. + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + // Evaluate the method the way the JVM verifier would do it. + simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + int codeLength = codeAttribute.u4codeLength; + + // Reset the code changes. + codeAttributeEditor.reset(codeLength); + + // Mark any unused method parameters on the stack. + if (DEBUG) System.out.println("Invocation simplification:"); + + for (int offset = 0; offset < codeLength; offset++) + { + if (partialEvaluator.isTraced(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, unusedParameterSimplifier); + } + } + + // Mark all essential instructions that have been encountered as used. + if (DEBUG) System.out.println("Usage initialization: "); + + maxMarkedOffset = -1; + + // The invocation of the "super" or "this" method inside a + // constructor is always necessary. + int superInitializationOffset = partialEvaluator.superInitializationOffset(); + if (superInitializationOffset != PartialEvaluator.NONE) + { + if (DEBUG) System.out.print("(super.)"); + + markInstruction(superInitializationOffset); + } + + // Also mark infinite loops and instructions that cause side effects. + for (int offset = 0; offset < codeLength; offset++) + { + if (partialEvaluator.isTraced(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + // Mark that the instruction is necessary if it is an infinite loop. + if (instruction.opcode == InstructionConstants.OP_GOTO && + ((BranchInstruction)instruction).branchOffset == 0) + { + if (DEBUG) System.out.print("(infinite loop)"); + markInstruction(offset); + } + + // Mark that the instruction is necessary if it has side effects. + else if (sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + instruction)) + { + markInstruction(offset); + } + } + } + if (DEBUG) System.out.println(); + + + // Globally mark instructions and their produced variables and stack + // entries on which necessary instructions depend. + // Instead of doing this recursively, we loop across all instructions, + // starting at the highest previously unmarked instruction that has + // been been marked. + if (DEBUG) System.out.println("Usage marking:"); + + while (maxMarkedOffset >= 0) + { + int offset = maxMarkedOffset; + + maxMarkedOffset = offset - 1; + + if (partialEvaluator.isTraced(offset)) + { + if (isInstructionNecessary(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, producerMarker); + } + + // Check if this instruction is a branch origin from a branch + // that straddles some marked code. + markStraddlingBranches(offset, + partialEvaluator.branchTargets(offset), + true); + + // Check if this instruction is a branch target from a branch + // that straddles some marked code. + markStraddlingBranches(offset, + partialEvaluator.branchOrigins(offset), + false); + } + + if (DEBUG) + { + if (maxMarkedOffset > offset) + { + System.out.println(" -> "+maxMarkedOffset); + } + } + } + if (DEBUG) System.out.println(); + + + // Mark variable initializations, even if they aren't strictly necessary. + // The virtual machine's verification step is not smart enough to see + // this, and may complain otherwise. + if (DEBUG) System.out.println("Initialization marking: "); + + for (int offset = 0; offset < codeLength; offset++) + { + if (isInstructionNecessary(offset)) + { + // Mark initializations of the required instruction. + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, variableInitializationMarker); + } + } + if (DEBUG) System.out.println(); + + + // Locally fix instructions, in order to keep the stack consistent. + if (DEBUG) System.out.println("Stack consistency fixing:"); + + maxMarkedOffset = codeLength - 1; + + while (maxMarkedOffset >= 0) + { + int offset = maxMarkedOffset; + + maxMarkedOffset = offset - 1; + + if (partialEvaluator.isTraced(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, stackConsistencyFixer); + + // Check if this instruction is a branch origin from a branch + // that straddles some marked code. + markStraddlingBranches(offset, + partialEvaluator.branchTargets(offset), + true); + + // Check if this instruction is a branch target from a branch + // that straddles some marked code. + markStraddlingBranches(offset, + partialEvaluator.branchOrigins(offset), + false); + } + } + if (DEBUG) System.out.println(); + + + // Replace traced but unmarked backward branches by infinite loops. + // The virtual machine's verification step is not smart enough to see + // the code isn't reachable, and may complain otherwise. + // Any clearly unreachable code will still be removed elsewhere. + if (DEBUG) System.out.println("Infinite loop fixing:"); + + for (int offset = 0; offset < codeLength; offset++) + { + // Is it a traced but unmarked backward branch, without an unmarked + // straddling forward branch? Note that this is still a heuristic. + if (partialEvaluator.isTraced(offset) && + !isInstructionNecessary(offset) && + isAllSmallerThanOrEqual(partialEvaluator.branchTargets(offset), + offset) && + !isAnyUnnecessaryInstructionBranchingOver(lastNecessaryInstructionOffset(offset), + offset)) + { + replaceByInfiniteLoop(clazz, offset); + } + } + if (DEBUG) System.out.println(); + + + // Insert infinite loops after jumps to subroutines that don't return. + // The virtual machine's verification step is not smart enough to see + // the code isn't reachable, and may complain otherwise. + if (DEBUG) System.out.println("Non-returning subroutine fixing:"); + + for (int offset = 0; offset < codeLength; offset++) + { + // Is it a traced but unmarked backward branch, without an unmarked + // straddling forward branch? Note that this is still a heuristic. + if (isInstructionNecessary(offset) && + partialEvaluator.isSubroutineInvocation(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + int nextOffset = offset + instruction.length(offset); + if (!isInstructionNecessary(nextOffset)) + { + replaceByInfiniteLoop(clazz, nextOffset); + } + } + } + if (DEBUG) System.out.println(); + + + // Delete all instructions that are not used. + int offset = 0; + do + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + if (!isInstructionNecessary(offset)) + { + codeAttributeEditor.clearModifications(offset); + codeAttributeEditor.deleteInstruction(offset); + + // Visit the instruction, if required. + if (extraDeletedInstructionVisitor != null) + { + instruction.accept(clazz, method, codeAttribute, offset, extraDeletedInstructionVisitor); + } + } + + offset += instruction.length(offset); + } + while (offset < codeLength); + + + if (DEBUG_RESULTS) + { + System.out.println("Simplification results:"); + + offset = 0; + do + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + System.out.println((isInstructionNecessary(offset) ? " + " : " - ")+instruction.toString(offset)); + + if (partialEvaluator.isTraced(offset)) + { + int initializationOffset = partialEvaluator.initializationOffset(offset); + if (initializationOffset != PartialEvaluator.NONE) + { + System.out.println(" is to be initialized at ["+initializationOffset+"]"); + } + + InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); + if (branchTargets != null) + { + System.out.println(" has overall been branching to "+branchTargets); + } + + boolean deleted = codeAttributeEditor.deleted[offset]; + if (isInstructionNecessary(offset) && deleted) + { + System.out.println(" is deleted"); + } + + Instruction preInsertion = codeAttributeEditor.preInsertions[offset]; + if (preInsertion != null) + { + System.out.println(" is preceded by: "+preInsertion); + } + + Instruction replacement = codeAttributeEditor.replacements[offset]; + if (replacement != null) + { + System.out.println(" is replaced by: "+replacement); + } + + Instruction postInsertion = codeAttributeEditor.postInsertions[offset]; + if (postInsertion != null) + { + System.out.println(" is followed by: "+postInsertion); + } + } + + offset += instruction.length(offset); + } + while (offset < codeLength); + } + + // Apply all accumulated changes to the code. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + + + /** + * This MemberVisitor marks stack entries that aren't necessary because + * parameters aren't used in the methods that are visited. + */ + private class MyUnusedParameterSimplifier + extends SimplifiedVisitor + implements InstructionVisitor, + ConstantVisitor, + MemberVisitor + { + private int invocationOffset; + private ConstantInstruction invocationInstruction; + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + this.invocationOffset = offset; + this.invocationInstruction = constantInstruction; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + refConstant.referencedMemberAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) {} + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Get the total size of the parameters. + int parameterSize = ParameterUsageMarker.getParameterSize(programMethod); + + // Make the method invocation static, if possible. + if ((programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0 && + !ParameterUsageMarker.isParameterUsed(programMethod, 0)) + { + replaceByStaticInvocation(programClass, + invocationOffset, + invocationInstruction); + } + + // Remove unused parameters. + for (int index = 0; index < parameterSize; index++) + { + if (!ParameterUsageMarker.isParameterUsed(programMethod, index)) + { + TracedStack stack = + partialEvaluator.getStackBefore(invocationOffset); + + int stackIndex = stack.size() - parameterSize + index; + + if (DEBUG) + { + System.out.println(" ["+invocationOffset+"] Ignoring parameter #"+index+" of "+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] (stack entry #"+stackIndex+" ["+stack.getBottom(stackIndex)+"])"); + System.out.println(" Full stack: "+stack); + } + + markStackSimplificationBefore(invocationOffset, stackIndex); + } + } + } + } + + + /** + * This InstructionVisitor marks the producing instructions and produced + * variables and stack entries of the instructions that it visits. + * Simplified method arguments are ignored. + */ + private class MyProducerMarker + extends SimplifiedVisitor + implements InstructionVisitor + { + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + markStackProducers(clazz, offset, instruction); + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + case InstructionConstants.OP_DUP: + conditionallyMarkStackEntryProducers(offset, 0, 0); + conditionallyMarkStackEntryProducers(offset, 1, 0); + break; + case InstructionConstants.OP_DUP_X1: + conditionallyMarkStackEntryProducers(offset, 0, 0); + conditionallyMarkStackEntryProducers(offset, 1, 1); + conditionallyMarkStackEntryProducers(offset, 2, 0); + break; + case InstructionConstants.OP_DUP_X2: + conditionallyMarkStackEntryProducers(offset, 0, 0); + conditionallyMarkStackEntryProducers(offset, 1, 1); + conditionallyMarkStackEntryProducers(offset, 2, 2); + conditionallyMarkStackEntryProducers(offset, 3, 0); + break; + case InstructionConstants.OP_DUP2: + conditionallyMarkStackEntryProducers(offset, 0, 0); + conditionallyMarkStackEntryProducers(offset, 1, 1); + conditionallyMarkStackEntryProducers(offset, 2, 0); + conditionallyMarkStackEntryProducers(offset, 3, 1); + break; + case InstructionConstants.OP_DUP2_X1: + conditionallyMarkStackEntryProducers(offset, 0, 0); + conditionallyMarkStackEntryProducers(offset, 1, 1); + conditionallyMarkStackEntryProducers(offset, 2, 2); + conditionallyMarkStackEntryProducers(offset, 3, 0); + conditionallyMarkStackEntryProducers(offset, 4, 1); + break; + case InstructionConstants.OP_DUP2_X2: + conditionallyMarkStackEntryProducers(offset, 0, 0); + conditionallyMarkStackEntryProducers(offset, 1, 1); + conditionallyMarkStackEntryProducers(offset, 2, 2); + conditionallyMarkStackEntryProducers(offset, 3, 3); + conditionallyMarkStackEntryProducers(offset, 4, 0); + conditionallyMarkStackEntryProducers(offset, 5, 1); + break; + case InstructionConstants.OP_SWAP: + conditionallyMarkStackEntryProducers(offset, 0, 1); + conditionallyMarkStackEntryProducers(offset, 1, 0); + break; + default: + markStackProducers(clazz, offset, simpleInstruction); + break; + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + // Is the variable being loaded or incremented? + if (variableInstruction.isLoad()) + { + markVariableProducers(offset, variableInstruction.variableIndex); + } + else + { + markStackProducers(clazz, offset, variableInstruction); + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Mark the initializer invocation, if this is a 'new' instruction. + if (constantInstruction.opcode == InstructionConstants.OP_NEW) + { + markInitialization(offset); + } + + markStackProducers(clazz, offset, constantInstruction); + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + // Explicitly mark the produced stack entry of a 'jsr' instruction, + // because the consuming 'astore' instruction of the subroutine is + // cleared every time it is traced. + if (branchInstruction.opcode == InstructionConstants.OP_JSR || + branchInstruction.opcode == InstructionConstants.OP_JSR_W) + { + markStackEntryAfter(offset, 0); + } + else + { + markStackProducers(clazz, offset, branchInstruction); + } + } + } + + + /** + * This InstructionVisitor marks variable initializations that are + * necessary to appease the JVM. + */ + private class MyVariableInitializationMarker + extends SimplifiedVisitor + implements InstructionVisitor + { + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + // Is the variable being loaded or incremented? + if (variableInstruction.isLoad()) + { + // Mark any variable initializations for this variable load that + // are required according to the JVM. + markVariableInitializers(offset, variableInstruction.variableIndex); + } + } + } + + + /** + * This InstructionVisitor fixes instructions locally, popping any unused + * produced stack entries after marked instructions, and popping produced + * stack entries and pushing missing stack entries instead of unmarked + * instructions. + */ + private class MyStackConsistencyFixer + extends SimplifiedVisitor + implements InstructionVisitor + { + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Has the instruction been marked? + if (isInstructionNecessary(offset)) + { + // Check all stack entries that are popped. + // Typical case: a freshly marked variable initialization that + // requires some value on the stack. + int popCount = instruction.stackPopCount(clazz); + if (popCount > 0) + { + TracedStack tracedStack = + partialEvaluator.getStackBefore(offset); + + int stackSize = tracedStack.size(); + + int requiredPushCount = 0; + for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) + { + if (!isStackSimplifiedBefore(offset, stackIndex)) + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); + } + else + { + // Remember to push it. + requiredPushCount++; + } + } + } + + // Push some necessary stack entries. + if (requiredPushCount > 0) + { + if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset)); + + if (requiredPushCount > (instruction.isCategory2() ? 2 : 1)) + { + throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"] at ["+offset+"]"); + } + + insertPushInstructions(offset, false, tracedStack.getTop(0).computationalType()); + } + } + + // Check all other stack entries, if this is a return + // instruction. + // Typical case: the code returns, but there are still other + // entries left on the stack. These have to be consistent. + InstructionOffsetValue branchTargets = + partialEvaluator.branchTargets(offset); + if (branchTargets != null && + branchTargets.instructionOffsetCount() == 0) + { + TracedStack tracedStack = + partialEvaluator.getStackBefore(offset); + + int unpoppedStackSize = tracedStack.size() - popCount; + + for (int stackIndex = 0; stackIndex < unpoppedStackSize; stackIndex++) + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); + } + } + } + + // Check all stack entries that are pushed. + // Typical case: a return value that wasn't really required and + // that should be popped. + int pushCount = instruction.stackPushCount(clazz); + if (pushCount > 0) + { + TracedStack tracedStack = + partialEvaluator.getStackAfter(offset); + + int stackSize = tracedStack.size(); + + int requiredPopCount = 0; + for (int stackIndex = stackSize - pushCount; stackIndex < stackSize; stackIndex++) + { + // Is the stack entry required by consumers? + if (!isStackEntryNecessaryAfter(offset, stackIndex)) + { + // Remember to pop it. + requiredPopCount++; + } + } + + // Pop the unnecessary stack entries. + if (requiredPopCount > 0) + { + if (DEBUG) System.out.println(" Inserting after marked producer "+instruction.toString(offset)); + + insertPopInstructions(offset, false, requiredPopCount); + } + } + } + else + { + // Check all stack entries that would be popped. + // Typical case: a stack value that is required elsewhere and + // that still has to be popped. + int popCount = instruction.stackPopCount(clazz); + if (popCount > 0) + { + TracedStack tracedStack = + partialEvaluator.getStackBefore(offset); + + int stackSize = tracedStack.size(); + + int expectedPopCount = 0; + for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); + + // Remember to pop it. + expectedPopCount++; + } + } + + // Pop the unnecessary stack entries. + if (expectedPopCount > 0) + { + if (DEBUG) System.out.println(" Replacing unmarked consumer "+instruction.toString(offset)); + + insertPopInstructions(offset, true, expectedPopCount); + } + } + + // Check all stack entries that would be pushed. + // Typical case: never. + int pushCount = instruction.stackPushCount(clazz); + if (pushCount > 0) + { + TracedStack tracedStack = + partialEvaluator.getStackAfter(offset); + + int stackSize = tracedStack.size(); + + int expectedPushCount = 0; + for (int stackIndex = stackSize - pushCount; stackIndex < stackSize; stackIndex++) + { + // Is the stack entry required by consumers? + if (isStackEntryNecessaryAfter(offset, stackIndex)) + { + // Remember to push it. + expectedPushCount++; + } + } + + // Push some necessary stack entries. + if (expectedPushCount > 0) + { + if (DEBUG) System.out.println(" Replacing unmarked producer "+instruction.toString(offset)); + + insertPushInstructions(offset, true, tracedStack.getTop(0).computationalType()); + } + } + } + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + if (isInstructionNecessary(offset) && + isDupOrSwap(simpleInstruction)) + { + int stackSizeBefore = partialEvaluator.getStackBefore(offset).size(); + + // Check all stack entries that are popped. + // Typical case: a freshly marked variable initialization that + // requires some value on the stack. + int popCount = simpleInstruction.stackPopCount(clazz); + if (popCount > 0) + { + for (int stackIndex = stackSizeBefore - popCount; stackIndex < stackSizeBefore; stackIndex++) + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); + } + } + } + + int topBefore = stackSizeBefore - 1; + int topAfter = partialEvaluator.getStackAfter(offset).size() - 1; + + byte oldOpcode = simpleInstruction.opcode; + + // Simplify the dup/swap instruction if possible. + int newOpcodes = fixDupSwap(offset, oldOpcode, topBefore, topAfter); + + // Did we find a suitabe (extended) opcode? + if (newOpcodes == UNSUPPORTED) + { + // We can't easily emulate some constructs. + throw new UnsupportedOperationException("Can't handle "+simpleInstruction.toString()+" instruction at ["+offset +"]"); + } + + // Is there a single replacement opcode? + if ((newOpcodes & ~0xff) == 0) + { + byte newOpcode = (byte)newOpcodes; + + if (newOpcode == InstructionConstants.OP_NOP) + { + // Delete the instruction. + codeAttributeEditor.deleteInstruction(offset); + + if (extraDeletedInstructionVisitor != null) + { + extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, offset, null); + } + + if (DEBUG) System.out.println(" Deleting marked instruction "+simpleInstruction.toString(offset)); + } + else if (newOpcode == oldOpcode) + { + // Leave the instruction unchanged. + codeAttributeEditor.undeleteInstruction(offset); + + if (DEBUG) System.out.println(" Marking unchanged instruction "+simpleInstruction.toString(offset)); + } + else + { + // Replace the instruction. + Instruction replacementInstruction = new SimpleInstruction(newOpcode); + codeAttributeEditor.replaceInstruction(offset, + replacementInstruction); + + if (DEBUG) System.out.println(" Replacing instruction "+simpleInstruction.toString(offset)+" by "+replacementInstruction.toString()); + } + } + else + { + // Collect the replacement instructions. + Instruction[] replacementInstructions = new Instruction[4]; + + if (DEBUG) System.out.println(" Replacing instruction "+simpleInstruction.toString(offset)+" by"); + int count = 0; + while (newOpcodes != 0) + { + SimpleInstruction replacementInstruction = new SimpleInstruction((byte)newOpcodes); + replacementInstructions[count++] = replacementInstruction; + + if (DEBUG) System.out.println(" "+replacementInstruction.toString()); + newOpcodes >>>= 8; + } + + // Create a properly sized array. + if (count < 4) + { + Instruction[] newInstructions = new Instruction[count]; + System.arraycopy(replacementInstructions, 0, newInstructions, 0, count); + replacementInstructions = newInstructions; + } + + codeAttributeEditor.replaceInstruction(offset, + replacementInstructions); + } + } + else + { + visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + } + } + + + /** + * Returns a dup/swap opcode that is corrected for the stack entries + * that are present before the instruction and necessary after the + * instruction. The returned integer opcode may contain multiple byte + * opcodes (least significant byte first). + * @param instructionOffset the offset of the dup/swap instruction. + * @param dupSwapOpcode the original dup/swap opcode. + * @param topBefore the index of the top stack entry before + * the instruction (counting from the bottom). + * @param topAfter the index of the top stack entry after + * the instruction (counting from the bottom). + * @return the corrected opcode. + */ + private int fixDupSwap(int instructionOffset, + byte dupSwapOpcode, + int topBefore, + int topAfter) + { + switch (dupSwapOpcode) + { + case InstructionConstants.OP_DUP: return fixedDup (instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP_X1: return fixedDup_x1 (instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP_X2: return fixedDup_x2 (instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP2: return fixedDup2 (instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP2_X1: return fixedDup2_x1(instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP2_X2: return fixedDup2_x2(instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_SWAP: return fixedSwap (instructionOffset, topBefore, topAfter); + default: throw new IllegalArgumentException("Not a dup/swap opcode ["+dupSwapOpcode+"]"); + } + } + + + private int fixedDup(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary0 ? + stackEntryNecessary1 ? DUP : // ...O -> ...OO + NOP : // ...O -> ...O + stackEntryNecessary1 ? NOP : // ...O -> ...O + stackEntryPresent0 ? POP : // ...O -> ... + NOP; // ... -> ... + } + + + private int fixedDup_x1(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary1 ? + stackEntryNecessary2 ? + stackEntryNecessary0 ? DUP_X1 : // ...XO -> ...OXO + SWAP : // ...XO -> ...OX + // !stackEntryNecessary2 + stackEntryNecessary0 ? NOP : // ...XO -> ...XO + stackEntryPresent0 ? POP : // ...XO -> ...X + NOP : // ...X -> ...X + stackEntryPresent1 ? + stackEntryNecessary2 ? + stackEntryNecessary0 ? SWAP_POP_DUP : // ...XO -> ...OO + POP_X1 : // ...XO -> ...O + // !stackEntryNecessary2 + stackEntryNecessary0 ? POP_X1 : // ...XO -> ...O + stackEntryPresent0 ? POP2 : // ...XO -> ... + POP : // ...X -> ... + // !stackEntryPresent1 + stackEntryNecessary2 ? + stackEntryNecessary0 ? DUP : // ...O -> ...OO + NOP : // ...O -> ...O + // !stackEntryNecessary2 + stackEntryNecessary0 ? NOP : // ...O -> ...O + stackEntryPresent0 ? POP : // ...O -> ... + NOP; // ... -> ... + } + + + private int fixedDup_x2(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); + boolean stackEntryPresent2 = isStackEntryPresentBefore(instructionOffset, topBefore - 2); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 3); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary1 ? + stackEntryNecessary2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? DUP_X2 : // ...XYO -> ...OXYO + MOV_X2 : // ...XYO -> ...OXY + // !stackEntryNecessary3 + stackEntryNecessary0 ? NOP : // ...XYO -> ...XYO + stackEntryPresent0 ? POP : // ...XYO -> ...XY + NOP : // ...XY -> ...XY + stackEntryPresent2 ? + stackEntryNecessary3 ? + // stackEntryNecessary0 ? UNSUPPORTED : // ...XYO -> ...OYO + UNSUPPORTED : // ...XYO -> ...OY + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP_X2 : // ...XYO -> ...YO + stackEntryPresent0 ? POP_SWAP_POP : // ...XYO -> ...Y + POP_X1 : // ...XY -> ...Y + // !stackEntryPresent2 + stackEntryNecessary3 ? + stackEntryNecessary0 ? DUP_X1 : // ...YO -> ...OYO + SWAP : // ...YO -> ...OY + // !stackEntryNecessary3 + stackEntryNecessary0 ? NOP : // ...YO -> ...YO + stackEntryPresent0 ? POP : // ...YO -> ...Y + NOP : // ...Y -> ...Y + stackEntryPresent1 ? + stackEntryNecessary2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? SWAP_POP_DUP_X1 : // ...XYO -> ...OXO + DUP_X2_POP2 : // ...XYO -> ...OX + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP_X1 : // ...XYO -> ...XO + stackEntryPresent0 ? POP2 : // ...XYO -> ...X + POP : // ...XY -> ...X + stackEntryPresent2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? UNSUPPORTED : // ...XYO -> ...OO + POP2_X1 : // ...XYO -> ...O + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP2_X1 : // ...XYO -> ...O + stackEntryPresent0 ? POP3 : // ...XYO -> ... + POP2 : // ...XY -> ... + // !stackEntryPresent2 + stackEntryNecessary3 ? + stackEntryNecessary0 ? SWAP_POP_DUP : // ...YO -> ...OO + POP_X1 : // ...YO -> ...O + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP_X1 : // ...YO -> ...O + stackEntryPresent0 ? POP2 : // ...YO -> ... + POP : // ...Y -> ... + // !stackEntryPresent1 + stackEntryNecessary2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? DUP_X1 : // ...XO -> ...OXO + SWAP : // ...XO -> ...OX + // !stackEntryNecessary3 + stackEntryNecessary0 ? NOP : // ...XO -> ...XO + stackEntryPresent0 ? POP : // ...XO -> ...X + NOP : // ...X -> ...X + stackEntryPresent2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? SWAP_POP_DUP : // ...XO -> ...OO + POP_X1 : // ...XO -> ...O + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP_X1 : // ...XO -> ...O + stackEntryPresent0 ? POP2 : // ...XO -> ... + POP : // ...X -> ... + // !stackEntryPresent2 + stackEntryNecessary3 ? + stackEntryNecessary0 ? DUP : // ...O -> ...OO + NOP : // ...O -> ...O + // !stackEntryNecessary3 + stackEntryNecessary0 ? NOP : // ...O -> ...O + stackEntryPresent0 ? POP : // ...O -> ... + NOP; // ... -> ... + } + + + private int fixedDup2(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 3); + + return + stackEntryNecessary3 ? + stackEntryNecessary2 ? + stackEntryNecessary1 ? + stackEntryNecessary0 ? DUP2 : // ...AB -> ...ABAB + SWAP_DUP_X1 : // ...AB -> ...ABA + // !stackEntryNecessary1 + stackEntryNecessary0 ? DUP : // ...AB -> ...ABB + NOP : // ...AB -> ...AB + // !stackEntryNecessary2 + stackEntryNecessary1 ? + stackEntryNecessary0 ? SWAP_DUP_X1_SWAP : // ...AB -> ...AAB + stackEntryPresent0 ? POP_DUP : // ...AB -> ...AA + DUP : // ...A -> ...AA + // !stackEntryNecessary1 + stackEntryNecessary0 ? NOP : // ...AB -> ...AB + stackEntryPresent0 ? POP : // ...AB -> ...A + NOP : // ...A -> ...A + // !stackEntryNecessary3 + stackEntryNecessary2 ? + stackEntryNecessary1 ? + stackEntryNecessary0 ? DUP_X1 : // ...AB -> ...BAB + SWAP : // ...AB -> ...BA + stackEntryPresent1 ? + stackEntryNecessary0 ? SWAP_POP_DUP : // ...AB -> ...BB + POP_X1 : // ...AB -> ...B + // !stackEntryPresent1 + stackEntryNecessary0 ? POP : // ...B -> ...BB + NOP : // ...B -> ...B + // !stackEntryNecessary2 + stackEntryNecessary1 ? + stackEntryNecessary0 ? NOP : // ...AB -> ...AB + stackEntryPresent0 ? POP : // ...AB -> ...A + NOP : // ...A -> ...A + stackEntryPresent1 ? + stackEntryNecessary0 ? POP_X1 : // ...AB -> ...B + stackEntryPresent0 ? POP2 : // ...AB -> ... + POP : // ...A -> ... + // !stackEntryPresent1 + stackEntryNecessary0 ? NOP : // ...B -> ...B + stackEntryPresent0 ? POP : // ...B -> ... + NOP; // ... -> ... + } + + + private int fixedDup2_x1(int instructionOffset, int topBefore, int topAfter) + { + // We're currently assuming the value to be duplicated + // is a long or a double, taking up two slots, or at + // least consistent. + boolean stackEntriesPresent01 = isStackEntriesPresentBefore(instructionOffset, topBefore - 0, topBefore - 1); + boolean stackEntryPresent2 = isStackEntryPresentBefore( instructionOffset, topBefore - 2); + + boolean stackEntriesNecessary01 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 0, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 2); + boolean stackEntriesNecessary34 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 3, topAfter - 4); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary2 ? + stackEntriesNecessary34 ? + stackEntriesNecessary01 ? DUP2_X1 : // ...XAB -> ...ABXAB + MOV2_X1 : // ...XAB -> ...ABX + // !stackEntriesNecessary34 + stackEntriesNecessary01 ? NOP : // ...XAB -> ...XAB + stackEntriesPresent01 ? POP2 : // ...XAB -> ...X + NOP : // ...X -> ...X + stackEntryPresent2 ? + stackEntriesNecessary34 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XAB -> ...ABAB + POP_X2 : // ...XAB -> ...AB + // !stackEntriesNecessary34 + stackEntriesNecessary01 ? DUP2_X1_POP3 : // ...XAB -> ...AB + stackEntriesPresent01 ? POP3 : // ...XAB -> ... + POP : // ...X -> ... + // !stackEntryPresent2 + stackEntriesNecessary34 ? + stackEntriesNecessary01 ? DUP2 : // ...AB -> ...ABAB + NOP : // ...AB -> ...AB + // !stackEntriesNecessary34 + stackEntriesNecessary01 ? NOP : // ...AB -> ...AB + stackEntriesPresent01 ? POP2 : // ...AB -> ... + NOP; // ... -> ... + } + + + private int fixedDup2_x2(int instructionOffset, int topBefore, int topAfter) + { + // We're currently assuming the value to be duplicated + // is a long or a double, taking up two slots, or at + // least consistent. + boolean stackEntriesPresent01 = isStackEntriesPresentBefore(instructionOffset, topBefore - 0, topBefore - 1); + boolean stackEntryPresent2 = isStackEntryPresentBefore( instructionOffset, topBefore - 2); + boolean stackEntryPresent3 = isStackEntryPresentBefore( instructionOffset, topBefore - 3); + + boolean stackEntriesNecessary01 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 0, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 3); + boolean stackEntriesNecessary45 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 4, topAfter - 5); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary2 ? + stackEntryNecessary3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? DUP2_X2 : // ...XYAB -> ...ABXYAB + MOV2_X2 : // ...XYAB -> ...ABXY + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? NOP : // ...XYAB -> ...XYAB + stackEntriesPresent01 ? POP2 : // ...XYAB -> ...XY + NOP : // ...XY -> ...XY + stackEntryPresent3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABYAB + DUP2_X2_SWAP_POP : // ...XYAB -> ...ABY + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP_X3 : // ...XYAB -> ...YAB + stackEntriesPresent01 ? POP2_SWAP_POP : // ...XYAB -> ...Y + POP_X1 : // ...XY -> ...Y + // !stackEntryPresent3 + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? DUP2_X1 : // ...YAB -> ...ABYAB + MOV2_X1 : // ...YAB -> ...ABY + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? NOP : // ...YAB -> ...YAB + stackEntriesPresent01 ? POP2 : // ...YAB -> ...Y + NOP : // ...Y -> ...Y + stackEntryPresent2 ? + stackEntryNecessary3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABXAB + DUP2_X2_POP3 : // ...XYAB -> ...ABX + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP_X2 : // ...XYAB -> ...XAB + stackEntriesPresent01 ? POP3 : // ...XYAB -> ...X + POP : // ...XY -> ...X + stackEntryPresent3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABAB + POP2_X2 : // ...XYAB -> ...AB + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP2_X2 : // ...XYAB -> ...AB + stackEntriesPresent01 ? POP4 : // ...XYAB -> ... + POP2 : // ...XY -> ... + // !stackEntryPresent3 + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...YAB -> ...ABAB + POP_X2 : // ...YAB -> ...AB + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP_X2 : // ...YAB -> ...AB + stackEntriesPresent01 ? POP3 : // ...YAB -> ... + POP : // ...Y -> ... + // !stackEntryPresent2 + stackEntryNecessary3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? DUP2_X1 : // ...XAB -> ...ABXAB + MOV2_X1 : // ...XAB -> ...ABX + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? NOP : // ...XAB -> ...XAB + stackEntriesPresent01 ? POP2 : // ...XAB -> ...X + NOP : // ...X -> ...X + stackEntryPresent3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XAB -> ...ABAB + POP_X2 : // ...XAB -> ...AB + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP_X2 : // ...XAB -> ...AB + stackEntriesPresent01 ? POP3 : // ...XAB -> ... + POP : // ...X -> ... + // !stackEntryPresent3 + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? DUP2 : // ...AB -> ...ABAB + NOP : // ...AB -> ...AB + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? NOP : // ...AB -> ...AB + stackEntriesPresent01 ? POP2 : // ...AB -> ... + NOP; // ... -> ... + } + + + private int fixedSwap(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + + // Figure out which stack entries should be moved + // or removed. + return + stackEntryNecessary0 ? + stackEntryNecessary1 ? SWAP : // ...AB -> ...BA + stackEntryPresent0 ? POP : // ...AB -> ...A + NOP : // ...A -> ...A + stackEntryPresent1 ? POP_X1 : // ...AB -> ...B + NOP; // ...B -> ...B + } + } + + + // Small utility methods. + + /** + * Marks the producing instructions of the variable consumer at the given + * offset. + * @param consumerOffset the offset of the variable consumer. + * @param variableIndex the index of the variable that is loaded. + */ + private void markVariableProducers(int consumerOffset, + int variableIndex) + { + InstructionOffsetValue producerOffsets = + partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); + + if (producerOffsets != null) + { + int offsetCount = producerOffsets.instructionOffsetCount(); + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + // Make sure the variable and the instruction are marked + // at the producing offset. + int offset = producerOffsets.instructionOffset(offsetIndex); + + markInstruction(offset); + } + } + } + + + /** + * Marks the initializing instructions of the variable consumer at the given + * offset. + * @param consumerOffset the offset of the variable consumer. + * @param variableIndex the index of the variable that is loaded. + */ + private void markVariableInitializers(int consumerOffset, + int variableIndex) + { + InstructionOffsetValue producerOffsets = + simplePartialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); + + if (producerOffsets != null) + { + int offsetCount = producerOffsets.instructionOffsetCount(); + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + // Make sure the variable and the instruction are marked + // at the producing offset. + int offset = producerOffsets.instructionOffset(offsetIndex); + + if (!isInstructionNecessary(offset) && + isVariableInitialization(offset, variableIndex)) + { + if (DEBUG) System.out.print(" Marking initialization of v"+variableIndex+" at "); + + markInstruction(offset); + + if (DEBUG) System.out.println(); + } + } + } + } + + + /** + * Marks the stack entries and their producing instructions of the + * consumer at the given offset. + * @param clazz the containing class. + * @param consumerOffset the offset of the consumer. + * @param consumer the consumer of the stack entries. + */ + private void markStackProducers(Clazz clazz, + int consumerOffset, + Instruction consumer) + { + TracedStack tracedStack = + partialEvaluator.getStackBefore(consumerOffset); + + int stackSize = tracedStack.size(); + + // Mark the producers of the popped values. + int popCount = consumer.stackPopCount(clazz); + for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) + { + markStackEntryProducers(consumerOffset, stackIndex); + } + } + + + /** + * Marks the stack entry and the corresponding producing instructions + * of the consumer at the given offset, if the stack entry of the + * consumer is marked. + * @param consumerOffset the offset of the consumer. + * @param consumerTopStackIndex the index of the stack entry to be checked + * (counting from the top). + * @param producerTopStackIndex the index of the stack entry to be marked + * (counting from the top). + */ + private void conditionallyMarkStackEntryProducers(int consumerOffset, + int consumerTopStackIndex, + int producerTopStackIndex) + { + int consumerBottomStackIndex = partialEvaluator.getStackAfter(consumerOffset).size() - consumerTopStackIndex - 1; + + if (isStackEntryNecessaryAfter(consumerOffset, consumerBottomStackIndex)) + { + int producerBottomStackIndex = partialEvaluator.getStackBefore(consumerOffset).size() - producerTopStackIndex - 1; + + markStackEntryProducers(consumerOffset, producerBottomStackIndex); + } + } + + + /** + * Marks the stack entry and the corresponding producing instructions + * of the consumer at the given offset. + * @param consumerOffset the offset of the consumer. + * @param stackIndex the index of the stack entry to be marked + * (counting from the bottom). + */ + private void markStackEntryProducers(int consumerOffset, + int stackIndex) + { + if (!isStackSimplifiedBefore(consumerOffset, stackIndex)) + { + markStackEntryProducers(partialEvaluator.getStackBefore(consumerOffset).getBottomProducerValue(stackIndex).instructionOffsetValue(), + stackIndex); + } + } + + + /** + * Marks the stack entry and its producing instructions at the given + * offsets. + * @param producerOffsets the offsets of the producers to be marked. + * @param stackIndex the index of the stack entry to be marked + * (counting from the bottom). + */ + private void markStackEntryProducers(InstructionOffsetValue producerOffsets, + int stackIndex) + { + if (producerOffsets != null) + { + int offsetCount = producerOffsets.instructionOffsetCount(); + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + // Make sure the stack entry and the instruction are marked + // at the producing offset. + int offset = producerOffsets.instructionOffset(offsetIndex); + + markStackEntryAfter(offset, stackIndex); + markInstruction(offset); + } + } + } + + + /** + * Marks the stack entry and its initializing instruction + * ('invokespecial *.') for the given 'new' instruction offset. + * @param newInstructionOffset the offset of the 'new' instruction. + */ + private void markInitialization(int newInstructionOffset) + { + int initializationOffset = + partialEvaluator.initializationOffset(newInstructionOffset); + + TracedStack tracedStack = + partialEvaluator.getStackAfter(newInstructionOffset); + + markStackEntryAfter(initializationOffset, tracedStack.size() - 1); + markInstruction(initializationOffset); + } + + + /** + * Marks the branch instructions of straddling branches, if they straddle + * some code that has been marked. + * @param instructionOffset the offset of the branch origin or branch target. + * @param branchOffsets the offsets of the straddling branch targets + * or branch origins. + * @param isPointingToTargets true if the above offsets are + * branch targets, false if they + * are branch origins. + */ + private void markStraddlingBranches(int instructionOffset, + InstructionOffsetValue branchOffsets, + boolean isPointingToTargets) + { + if (branchOffsets != null) + { + // Loop over all branch offsets. + int branchCount = branchOffsets.instructionOffsetCount(); + for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) + { + // Is the branch straddling forward any necessary instructions? + int branchOffset = branchOffsets.instructionOffset(branchIndex); + + // Is the offset pointing to a branch origin or to a branch target? + if (isPointingToTargets) + { + markStraddlingBranch(instructionOffset, + branchOffset, + instructionOffset, + branchOffset); + } + else + { + markStraddlingBranch(instructionOffset, + branchOffset, + branchOffset, + instructionOffset); + } + } + } + } + + + private void markStraddlingBranch(int instructionOffsetStart, + int instructionOffsetEnd, + int branchOrigin, + int branchTarget) + { + if (!isInstructionNecessary(branchOrigin) && + isAnyInstructionNecessary(instructionOffsetStart, instructionOffsetEnd)) + { + if (DEBUG) System.out.print("["+branchOrigin+"->"+branchTarget+"]"); + + // Mark the branch instruction. + markInstruction(branchOrigin); + } + } + + + /** + * Pushes a specified type of stack entry before or at the given offset. + * The instruction is marked as necessary. + */ + private void insertPushInstructions(int offset, + boolean replace, + int computationalType) + { + // Mark this instruction. + markInstruction(offset); + + // Create a simple push instrucion. + Instruction replacementInstruction = + new SimpleInstruction(pushOpcode(computationalType)); + + if (DEBUG) System.out.println(": "+replacementInstruction.toString(offset)); + + // Replace or insert the push instruction. + if (replace) + { + // Replace the push instruction. + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + } + else + { + // Insert the push instruction. + codeAttributeEditor.insertBeforeInstruction(offset, replacementInstruction); + + if (extraAddedInstructionVisitor != null) + { + replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + + + /** + * Returns the opcode of a push instruction corresponding to the given + * computational type. + * @param computationalType the computational type to be pushed on the stack. + */ + private byte pushOpcode(int computationalType) + { + switch (computationalType) + { + case Value.TYPE_INTEGER: return InstructionConstants.OP_ICONST_0; + case Value.TYPE_LONG: return InstructionConstants.OP_LCONST_0; + case Value.TYPE_FLOAT: return InstructionConstants.OP_FCONST_0; + case Value.TYPE_DOUBLE: return InstructionConstants.OP_DCONST_0; + case Value.TYPE_REFERENCE: + case Value.TYPE_INSTRUCTION_OFFSET: return InstructionConstants.OP_ACONST_NULL; + } + + throw new IllegalArgumentException("No push opcode for computational type ["+computationalType+"]"); + } + + + /** + * Pops the given number of stack entries at or after the given offset. + * The instructions are marked as necessary. + */ + private void insertPopInstructions(int offset, boolean replace, int popCount) + { + // Mark this instruction. + markInstruction(offset); + + switch (popCount) + { + case 1: + { + // Replace or insert a single pop instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP); + + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, popInstruction); + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, popInstruction); + + if (extraAddedInstructionVisitor != null) + { + popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + break; + } + case 2: + { + // Replace or insert a single pop2 instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP2); + + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, popInstruction); + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, popInstruction); + + if (extraAddedInstructionVisitor != null) + { + popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + break; + } + default: + { + // Replace or insert the specified number of pop instructions. + Instruction[] popInstructions = + new Instruction[popCount / 2 + popCount % 2]; + + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP2); + + for (int index = 0; index < popCount / 2; index++) + { + popInstructions[index] = popInstruction; + } + + if (popCount % 2 == 1) + { + popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP); + + popInstructions[popCount / 2] = popInstruction; + } + + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, popInstructions); + + for (int index = 1; index < popInstructions.length; index++) + { + if (extraAddedInstructionVisitor != null) + { + popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, popInstructions); + + for (int index = 0; index < popInstructions.length; index++) + { + if (extraAddedInstructionVisitor != null) + { + popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + break; + } + } + } + + + /** + * Replaces the instruction at a given offset by a static invocation. + */ + private void replaceByStaticInvocation(Clazz clazz, + int offset, + ConstantInstruction constantInstruction) + { + // Remember the replacement instruction. + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, + constantInstruction.constantIndex); + + if (DEBUG) System.out.println(" Replacing by static invocation "+constantInstruction.toString(offset)+" -> "+replacementInstruction.toString()); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + } + + + /** + * Replaces the given instruction by an infinite loop. + */ + private void replaceByInfiniteLoop(Clazz clazz, + int offset) + { + if (DEBUG) System.out.println(" Inserting infinite loop at ["+offset+"]"); + + // Mark the instruction. + markInstruction(offset); + + // Replace the instruction by an infinite loop. + Instruction replacementInstruction = + new BranchInstruction(InstructionConstants.OP_GOTO, 0); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + } + + + // Small utility methods. + + /** + * Returns whether the given instruction is a dup or swap instruction + * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap). + */ + private boolean isDupOrSwap(Instruction instruction) + { + return instruction.opcode >= InstructionConstants.OP_DUP && + instruction.opcode <= InstructionConstants.OP_SWAP; + } + + + /** + * Returns whether the given instruction is a pop instruction + * (pop, pop2). + */ + private boolean isPop(Instruction instruction) + { + return instruction.opcode == InstructionConstants.OP_POP || + instruction.opcode == InstructionConstants.OP_POP2; + } + + + /** + * Returns whether any traced but unnecessary instruction between the two + * given offsets is branching over the second given offset. + */ + private boolean isAnyUnnecessaryInstructionBranchingOver(int instructionOffset1, + int instructionOffset2) + { + for (int offset = instructionOffset1; offset < instructionOffset2; offset++) + { + // Is it a traced but unmarked straddling branch? + if (partialEvaluator.isTraced(offset) && + !isInstructionNecessary(offset) && + isAnyLargerThan(partialEvaluator.branchTargets(offset), + instructionOffset2)) + { + return true; + } + } + + return false; + } + + + /** + * Returns whether all of the given instruction offsets (at least one) + * are smaller than or equal to the given offset. + */ + private boolean isAllSmallerThanOrEqual(InstructionOffsetValue instructionOffsets, + int instructionOffset) + { + if (instructionOffsets != null) + { + // Loop over all instruction offsets. + int branchCount = instructionOffsets.instructionOffsetCount(); + if (branchCount > 0) + { + for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) + { + // Is the offset larger than the reference offset? + if (instructionOffsets.instructionOffset(branchIndex) > instructionOffset) + { + return false; + } + } + + return true; + } + } + + return false; + } + + + /** + * Returns whether any of the given instruction offsets (at least one) + * is larger than the given offset. + */ + private boolean isAnyLargerThan(InstructionOffsetValue instructionOffsets, + int instructionOffset) + { + if (instructionOffsets != null) + { + // Loop over all instruction offsets. + int branchCount = instructionOffsets.instructionOffsetCount(); + if (branchCount > 0) + { + for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) + { + // Is the offset larger than the reference offset? + if (instructionOffsets.instructionOffset(branchIndex) > instructionOffset) + { + return true; + } + } + } + } + + return false; + } + + + /** + * Initializes the necessary data structure. + */ + private void initializeNecessary(CodeAttribute codeAttribute) + { + int codeLength = codeAttribute.u4codeLength; + int maxLocals = codeAttribute.u2maxLocals; + int maxStack = codeAttribute.u2maxStack; + + // Create new arrays for storing information at each instruction offset. + if (stacksNecessaryAfter.length < codeLength || + stacksNecessaryAfter[0].length < maxStack) + { + stacksNecessaryAfter = new boolean[codeLength][maxStack]; + } + else + { + for (int offset = 0; offset < codeLength; offset++) + { + Arrays.fill(stacksNecessaryAfter[offset], 0, maxStack, false); + } + } + + if (stacksSimplifiedBefore.length < codeLength || + stacksSimplifiedBefore[0].length < maxStack) + { + stacksSimplifiedBefore = new boolean[codeLength][maxStack]; + } + else + { + for (int offset = 0; offset < codeLength; offset++) + { + Arrays.fill(stacksSimplifiedBefore[offset], 0, maxStack, false); + } + } + + if (instructionsNecessary.length < codeLength) + { + instructionsNecessary = new boolean[codeLength]; + } + else + { + Arrays.fill(instructionsNecessary, 0, codeLength, false); + } + } + + + /** + * Returns whether the specified variable is initialized at the specified + * offset. + */ + private boolean isVariableInitialization(int instructionOffset, + int variableIndex) + { + // Wasn't the variable set yet? + Value valueBefore = partialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex); + if (valueBefore == null) + { + return true; + } + + // Is the computational type different now? + Value valueAfter = partialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex); + if (valueAfter.computationalType() != valueBefore.computationalType()) + { + return true; + } + + // Is the reference type different now? + if (valueAfter.computationalType() == Value.TYPE_REFERENCE && + (valueAfter.referenceValue().isNull() == Value.ALWAYS || + !valueAfter.referenceValue().getType().equals(valueBefore.referenceValue().getType()))) + { + return true; + } + + // Was the producer an argument (which may be removed)? + Value producersBefore = partialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex); + return producersBefore.instructionOffsetValue().instructionOffsetCount() == 1 && + producersBefore.instructionOffsetValue().instructionOffset(0) == PartialEvaluator.AT_METHOD_ENTRY; + } + + + /** + * Marks the stack entry after the given offset. + * @param instructionOffset the offset of the stack entry to be marked. + * @param stackIndex the index of the stack entry to be marked + * (counting from the bottom). + */ + private void markStackEntryAfter(int instructionOffset, + int stackIndex) + { + if (!isStackEntryNecessaryAfter(instructionOffset, stackIndex)) + { + if (DEBUG) System.out.print("["+instructionOffset+".s"+stackIndex+"],"); + + stacksNecessaryAfter[instructionOffset][stackIndex] = true; + + if (maxMarkedOffset < instructionOffset) + { + maxMarkedOffset = instructionOffset; + } + } + } + + + + /** + * Returns whether the stack specified entries before the given offset are + * present. + */ + private boolean isStackEntriesPresentBefore(int instructionOffset, + int stackIndex1, + int stackIndex2) + { + boolean present1 = isStackEntryPresentBefore(instructionOffset, stackIndex1); + boolean present2 = isStackEntryPresentBefore(instructionOffset, stackIndex2); + +// if (present1 ^ present2) +// { +// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions"); +// } + + return present1 || present2; + } + + + /** + * Returns whether the specified stack entry before the given offset is + * present. + * @param instructionOffset the offset of the stack entry to be checked. + * @param stackIndex the index of the stack entry to be checked + * (counting from the bottom). + */ + private boolean isStackEntryPresentBefore(int instructionOffset, + int stackIndex) + { + TracedStack tracedStack = + partialEvaluator.getStackBefore(instructionOffset); + + InstructionOffsetValue producerOffsets = + tracedStack.getBottomProducerValue(stackIndex).instructionOffsetValue(); + + return isAnyStackEntryNecessaryAfter(producerOffsets, stackIndex); + } + + + /** + * Returns whether the stack specified entries after the given offset are + * necessary. + */ + private boolean isStackEntriesNecessaryAfter(int instructionOffset, + int stackIndex1, + int stackIndex2) + { + boolean present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1); + boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2); + +// if (present1 ^ present2) +// { +// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions"); +// } + + return present1 || present2; + } + + + /** + * Returns whether any of the stack entries after the given offsets are + * necessary. + * @param instructionOffsets the offsets of the stack entries to be checked. + * @param stackIndex the index of the stack entries to be checked + * (counting from the bottom). + */ + private boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets, + int stackIndex) + { + int offsetCount = instructionOffsets.instructionOffsetCount(); + + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + if (isStackEntryNecessaryAfter(instructionOffsets.instructionOffset(offsetIndex), stackIndex)) + { + return true; + } + } + + return false; + } + + + /** + * Returns whether the specified stack entry after the given offset is + * necessary. + * @param instructionOffset the offset of the stack entry to be checked. + * @param stackIndex the index of the stack entry to be checked + * (counting from the bottom). + */ + private boolean isStackEntryNecessaryAfter(int instructionOffset, + int stackIndex) + { + return instructionOffset == PartialEvaluator.AT_CATCH_ENTRY || + stacksNecessaryAfter[instructionOffset][stackIndex]; + } + + + private void markStackSimplificationBefore(int instructionOffset, + int stackIndex) + { + stacksSimplifiedBefore[instructionOffset][stackIndex] = true; + } + + + private boolean isStackSimplifiedBefore(int instructionOffset, + int stackIndex) + { + return stacksSimplifiedBefore[instructionOffset][stackIndex]; + } + + + private void markInstruction(int instructionOffset) + { + if (!isInstructionNecessary(instructionOffset)) + { + if (DEBUG) System.out.print(instructionOffset+","); + + instructionsNecessary[instructionOffset] = true; + + if (maxMarkedOffset < instructionOffset) + { + maxMarkedOffset = instructionOffset; + } + } + } + + + private boolean isAnyInstructionNecessary(int instructionOffset1, + int instructionOffset2) + { + for (int instructionOffset = instructionOffset1; + instructionOffset < instructionOffset2; + instructionOffset++) + { + if (isInstructionNecessary(instructionOffset)) + { + return true; + } + } + + return false; + } + + + /** + * Returns the highest offset of an instruction that has been marked as + * necessary, before the given offset. + */ + private int lastNecessaryInstructionOffset(int instructionOffset) + { + for (int offset = instructionOffset-1; offset >= 0; offset--) + { + if (isInstructionNecessary(instructionOffset)) + { + return offset; + } + } + + return 0; + } + + + private boolean isInstructionNecessary(int instructionOffset) + { + return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY || + instructionsNecessary[instructionOffset]; + } +} diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java new file mode 100644 index 000000000..e6e73d909 --- /dev/null +++ b/src/proguard/optimize/evaluation/EvaluationSimplifier.java @@ -0,0 +1,988 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.info.*; + +/** + * This AttributeVisitor simplifies the code attributes that it visits, based + * on partial evaluation. + * + * @author Eric Lafortune + */ +public class EvaluationSimplifier +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor +{ + private static final int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f); + private static final long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0); + + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + private final InstructionVisitor extraInstructionVisitor; + + private final PartialEvaluator partialEvaluator; + private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true); + + + /** + * Creates a new EvaluationSimplifier. + */ + public EvaluationSimplifier() + { + this(new PartialEvaluator(), null); + } + + + /** + * Creates a new EvaluationSimplifier. + * @param partialEvaluator the partial evaluator that will + * execute the code and provide + * information about the results. + * @param extraInstructionVisitor an optional extra visitor for all + * simplified instructions. + */ + public EvaluationSimplifier(PartialEvaluator partialEvaluator, + InstructionVisitor extraInstructionVisitor) + { + this.partialEvaluator = partialEvaluator; + 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 evaluation simplifier 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 simplifying instructions after partial evaluation:"); + System.err.println(" Class = ["+clazz.getName()+"]"); + System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + System.err.println("Not optimizing this method"); + + if (DEBUG) + { + method.accept(clazz, new ClassPrinter()); + + throw ex; + } + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println(); + System.out.println("Class "+ClassUtil.externalClassName(clazz.getName())); + System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(), + 0, + method.getName(clazz), + method.getDescriptor(clazz))); + } + + // Evaluate the method. + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + int codeLength = codeAttribute.u4codeLength; + + // Reset the code changes. + codeAttributeEditor.reset(codeLength); + + // Replace any instructions that can be simplified. + for (int offset = 0; offset < codeLength; offset++) + { + if (partialEvaluator.isTraced(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, this); + } + } + + // Apply all accumulated changes to the code. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + case InstructionConstants.OP_IALOAD: + case InstructionConstants.OP_BALOAD: + case InstructionConstants.OP_CALOAD: + case InstructionConstants.OP_SALOAD: + case InstructionConstants.OP_IADD: + case InstructionConstants.OP_ISUB: + case InstructionConstants.OP_IMUL: + case InstructionConstants.OP_IDIV: + case InstructionConstants.OP_IREM: + case InstructionConstants.OP_INEG: + case InstructionConstants.OP_ISHL: + case InstructionConstants.OP_ISHR: + case InstructionConstants.OP_IUSHR: + case InstructionConstants.OP_IAND: + case InstructionConstants.OP_IOR: + case InstructionConstants.OP_IXOR: + case InstructionConstants.OP_L2I: + case InstructionConstants.OP_F2I: + case InstructionConstants.OP_D2I: + case InstructionConstants.OP_I2B: + case InstructionConstants.OP_I2C: + case InstructionConstants.OP_I2S: + replaceIntegerPushInstruction(clazz, offset, simpleInstruction); + break; + + case InstructionConstants.OP_LALOAD: + case InstructionConstants.OP_LADD: + case InstructionConstants.OP_LSUB: + case InstructionConstants.OP_LMUL: + case InstructionConstants.OP_LDIV: + case InstructionConstants.OP_LREM: + case InstructionConstants.OP_LNEG: + case InstructionConstants.OP_LSHL: + case InstructionConstants.OP_LSHR: + case InstructionConstants.OP_LUSHR: + case InstructionConstants.OP_LAND: + case InstructionConstants.OP_LOR: + case InstructionConstants.OP_LXOR: + case InstructionConstants.OP_I2L: + case InstructionConstants.OP_F2L: + case InstructionConstants.OP_D2L: + replaceLongPushInstruction(clazz, offset, simpleInstruction); + break; + + case InstructionConstants.OP_FALOAD: + case InstructionConstants.OP_FADD: + case InstructionConstants.OP_FSUB: + case InstructionConstants.OP_FMUL: + case InstructionConstants.OP_FDIV: + case InstructionConstants.OP_FREM: + case InstructionConstants.OP_FNEG: + case InstructionConstants.OP_I2F: + case InstructionConstants.OP_L2F: + case InstructionConstants.OP_D2F: + replaceFloatPushInstruction(clazz, offset, simpleInstruction); + break; + + case InstructionConstants.OP_DALOAD: + case InstructionConstants.OP_DADD: + case InstructionConstants.OP_DSUB: + case InstructionConstants.OP_DMUL: + case InstructionConstants.OP_DDIV: + case InstructionConstants.OP_DREM: + case InstructionConstants.OP_DNEG: + case InstructionConstants.OP_I2D: + case InstructionConstants.OP_L2D: + case InstructionConstants.OP_F2D: + replaceDoublePushInstruction(clazz, offset, simpleInstruction); + break; + + case InstructionConstants.OP_AALOAD: + replaceReferencePushInstruction(clazz, offset, simpleInstruction); + break; + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + int variableIndex = variableInstruction.variableIndex; + + switch (variableInstruction.opcode) + { + case InstructionConstants.OP_ILOAD: + case InstructionConstants.OP_ILOAD_0: + case InstructionConstants.OP_ILOAD_1: + case InstructionConstants.OP_ILOAD_2: + case InstructionConstants.OP_ILOAD_3: + replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex); + break; + + case InstructionConstants.OP_LLOAD: + case InstructionConstants.OP_LLOAD_0: + case InstructionConstants.OP_LLOAD_1: + case InstructionConstants.OP_LLOAD_2: + case InstructionConstants.OP_LLOAD_3: + replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex); + break; + + case InstructionConstants.OP_FLOAD: + case InstructionConstants.OP_FLOAD_0: + case InstructionConstants.OP_FLOAD_1: + case InstructionConstants.OP_FLOAD_2: + case InstructionConstants.OP_FLOAD_3: + replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex); + break; + + case InstructionConstants.OP_DLOAD: + case InstructionConstants.OP_DLOAD_0: + case InstructionConstants.OP_DLOAD_1: + case InstructionConstants.OP_DLOAD_2: + case InstructionConstants.OP_DLOAD_3: + replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex); + break; + + case InstructionConstants.OP_ALOAD: + case InstructionConstants.OP_ALOAD_0: + case InstructionConstants.OP_ALOAD_1: + case InstructionConstants.OP_ALOAD_2: + case InstructionConstants.OP_ALOAD_3: + replaceReferencePushInstruction(clazz, offset, variableInstruction); + break; + + case InstructionConstants.OP_ASTORE: + case InstructionConstants.OP_ASTORE_0: + case InstructionConstants.OP_ASTORE_1: + case InstructionConstants.OP_ASTORE_2: + case InstructionConstants.OP_ASTORE_3: + deleteReferencePopInstruction(clazz, offset, variableInstruction); + break; + + case InstructionConstants.OP_RET: + replaceBranchInstruction(clazz, offset, variableInstruction); + break; + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_GETFIELD: + replaceAnyPushInstruction(clazz, offset, constantInstruction); + break; + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + if (constantInstruction.stackPushCount(clazz) > 0 && + !sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + constantInstruction)) + { + replaceAnyPushInstruction(clazz, offset, constantInstruction); + } + + break; + + case InstructionConstants.OP_CHECKCAST: + replaceReferencePushInstruction(clazz, offset, constantInstruction); + break; + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + switch (branchInstruction.opcode) + { + case InstructionConstants.OP_GOTO: + case InstructionConstants.OP_GOTO_W: + // Don't replace unconditional branches. + break; + + case InstructionConstants.OP_JSR: + case InstructionConstants.OP_JSR_W: + replaceJsrInstruction(clazz, offset, branchInstruction); + break; + + default: + replaceBranchInstruction(clazz, offset, branchInstruction); + break; + } + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + // First try to simplify it to a simple branch. + replaceBranchInstruction(clazz, offset, switchInstruction); + + // Otherwise make sure all branch targets are valid. + if (!codeAttributeEditor.isModified(offset)) + { + replaceSwitchInstruction(clazz, offset, switchInstruction); + } + } + + + // Small utility methods. + + /** + * Replaces the push instruction at the given offset by a simpler push + * instruction, if possible. + */ + private void replaceAnyPushInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); + if (pushedValue.isParticular()) + { + switch (pushedValue.computationalType()) + { + case Value.TYPE_INTEGER: + replaceIntegerPushInstruction(clazz, offset, instruction); + break; + case Value.TYPE_LONG: + replaceLongPushInstruction(clazz, offset, instruction); + break; + case Value.TYPE_FLOAT: + replaceFloatPushInstruction(clazz, offset, instruction); + break; + case Value.TYPE_DOUBLE: + replaceDoublePushInstruction(clazz, offset, instruction); + break; + case Value.TYPE_REFERENCE: + replaceReferencePushInstruction(clazz, offset, instruction); + break; + } + } + } + + + /** + * Replaces the integer pushing instruction at the given offset by a simpler + * push instruction, if possible. + */ + private void replaceIntegerPushInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + replaceIntegerPushInstruction(clazz, + offset, + instruction, + partialEvaluator.getVariablesBefore(offset).size()); + } + + + /** + * Replaces the integer pushing instruction at the given offset by a simpler + * push instruction, if possible. + */ + private void replaceIntegerPushInstruction(Clazz clazz, + int offset, + Instruction instruction, + int maxVariableIndex) + { + Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); + if (pushedValue.isParticular()) + { + // Push a constant instead. + int value = pushedValue.integerValue().value(); + if ((short)value == value) + { + replaceConstantPushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_SIPUSH, + value); + } + else + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor((ProgramClass)clazz); + + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_LDC, + constantPoolEditor.addIntegerConstant(value)); + + replaceInstruction(clazz, offset, instruction, replacementInstruction); + } + } + else if (pushedValue.isSpecific()) + { + // Load an equivalent lower-numbered variable instead, if any. + TracedVariables variables = partialEvaluator.getVariablesBefore(offset); + for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) + { + if (pushedValue.equals(variables.load(variableIndex))) + { + replaceVariablePushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_ILOAD, + variableIndex); + break; + } + } + } + } + + + /** + * Replaces the long pushing instruction at the given offset by a simpler + * push instruction, if possible. + */ + private void replaceLongPushInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + replaceLongPushInstruction(clazz, + offset, + instruction, + partialEvaluator.getVariablesBefore(offset).size()); + } + + + /** + * Replaces the long pushing instruction at the given offset by a simpler + * push instruction, if possible. + */ + private void replaceLongPushInstruction(Clazz clazz, + int offset, + Instruction instruction, + int maxVariableIndex) + { + Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); + if (pushedValue.isParticular()) + { + // Push a constant instead. + long value = pushedValue.longValue().value(); + if (value == 0L || + value == 1L) + { + replaceConstantPushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_LCONST_0, + (int)value); + } + else + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor((ProgramClass)clazz); + + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_LDC2_W, + constantPoolEditor.addLongConstant(value)); + + replaceInstruction(clazz, offset, instruction, replacementInstruction); + } + } + else if (pushedValue.isSpecific()) + { + // Load an equivalent lower-numbered variable instead, if any. + TracedVariables variables = partialEvaluator.getVariablesBefore(offset); + for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) + { + // Note that we have to check the second part as well. + if (pushedValue.equals(variables.load(variableIndex)) && + variables.load(variableIndex + 1) != null && + variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP) + { + replaceVariablePushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_LLOAD, + variableIndex); + } + } + } + } + + + /** + * Replaces the float pushing instruction at the given offset by a simpler + * push instruction, if possible. + */ + private void replaceFloatPushInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + replaceFloatPushInstruction(clazz, + offset, + instruction, + partialEvaluator.getVariablesBefore(offset).size()); + } + + + /** + * Replaces the float pushing instruction at the given offset by a simpler + * push instruction, if possible. + */ + private void replaceFloatPushInstruction(Clazz clazz, + int offset, + Instruction instruction, + int maxVariableIndex) + { + Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); + if (pushedValue.isParticular()) + { + // Push a constant instead. + // Make sure to distinguish between +0.0 and -0.0. + float value = pushedValue.floatValue().value(); + if (value == 0.0f && Float.floatToIntBits(value) == POS_ZERO_FLOAT_BITS || + value == 1.0f || + value == 2.0f) + { + replaceConstantPushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_FCONST_0, + (int)value); + } + else + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor((ProgramClass)clazz); + + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_LDC, + constantPoolEditor.addFloatConstant(value)); + + replaceInstruction(clazz, offset, instruction, replacementInstruction); + } + } + else if (pushedValue.isSpecific()) + { + // Load an equivalent lower-numbered variable instead, if any. + TracedVariables variables = partialEvaluator.getVariablesBefore(offset); + for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) + { + if (pushedValue.equals(variables.load(variableIndex))) + { + replaceVariablePushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_FLOAD, + variableIndex); + } + } + } + } + + + /** + * Replaces the double pushing instruction at the given offset by a simpler + * push instruction, if possible. + */ + private void replaceDoublePushInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + replaceDoublePushInstruction(clazz, + offset, + instruction, + partialEvaluator.getVariablesBefore(offset).size()); + } + + + /** + * Replaces the double pushing instruction at the given offset by a simpler + * push instruction, if possible. + */ + private void replaceDoublePushInstruction(Clazz clazz, + int offset, + Instruction instruction, + int maxVariableIndex) + { + Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); + if (pushedValue.isParticular()) + { + // Push a constant instead. + // Make sure to distinguish between +0.0 and -0.0. + double value = pushedValue.doubleValue().value(); + if (value == 0.0 && Double.doubleToLongBits(value) == POS_ZERO_DOUBLE_BITS || + value == 1.0) + { + replaceConstantPushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_DCONST_0, + (int)value); + } + else + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor((ProgramClass)clazz); + + Instruction replacementInstruction = + new ConstantInstruction(InstructionConstants.OP_LDC2_W, + constantPoolEditor.addDoubleConstant(value)); + + replaceInstruction(clazz, offset, instruction, replacementInstruction); + } + } + else if (pushedValue.isSpecific()) + { + // Load an equivalent lower-numbered variable instead, if any. + TracedVariables variables = partialEvaluator.getVariablesBefore(offset); + for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) + { + // Note that we have to check the second part as well. + if (pushedValue.equals(variables.load(variableIndex)) && + variables.load(variableIndex + 1) != null && + variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP) + { + replaceVariablePushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_DLOAD, + variableIndex); + } + } + } + } + + + /** + * Replaces the reference pushing instruction at the given offset by a + * simpler push instruction, if possible. + */ + private void replaceReferencePushInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); + if (pushedValue.isParticular()) + { + // A reference value can only be specific if it is null. + replaceConstantPushInstruction(clazz, + offset, + instruction, + InstructionConstants.OP_ACONST_NULL, + 0); + } + } + + + /** + * Replaces the instruction at a given offset by a given push instruction + * of a constant. + */ + private void replaceConstantPushInstruction(Clazz clazz, + int offset, + Instruction instruction, + byte replacementOpcode, + int value) + { + Instruction replacementInstruction = + new SimpleInstruction(replacementOpcode, value); + + replaceInstruction(clazz, offset, instruction, replacementInstruction); + } + + + /** + * Replaces the instruction at a given offset by a given push instruction + * of a variable. + */ + private void replaceVariablePushInstruction(Clazz clazz, + int offset, + Instruction instruction, + byte replacementOpcode, + int variableIndex) + { + Instruction replacementInstruction = + new VariableInstruction(replacementOpcode, variableIndex); + + replaceInstruction(clazz, offset, instruction, replacementInstruction); + } + + + /** + * Replaces the given 'jsr' instruction by a simpler branch instruction, + * if it jumps to a subroutine that doesn't return or a subroutine that + * is only called from one place. + */ + private void replaceJsrInstruction(Clazz clazz, + int offset, + BranchInstruction branchInstruction) + { + // Is the subroutine ever returning? + int subroutineStart = offset + branchInstruction.branchOffset; + if (!partialEvaluator.isSubroutineReturning(subroutineStart) || + partialEvaluator.branchOrigins(subroutineStart).instructionOffsetCount() == 1) + { + // All 'jsr' instructions to this subroutine can be replaced + // by unconditional branch instructions. + replaceBranchInstruction(clazz, offset, branchInstruction); + } + else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset))) + { + // We have to make sure the instruction after this 'jsr' + // instruction is valid, even if it is never reached. + replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction); + } + } + + + /** + * Deletes the reference popping instruction at the given offset, if + * it is at the start of a subroutine that doesn't return or a subroutine + * that is only called from one place. + */ + private void deleteReferencePopInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + if (partialEvaluator.isSubroutineStart(offset) && + (!partialEvaluator.isSubroutineReturning(offset) || + partialEvaluator.branchOrigins(offset).instructionOffsetCount() == 1)) + { + if (DEBUG) System.out.println(" Deleting store of subroutine return address "+instruction.toString(offset)); + + // A reference value can only be specific if it is null. + codeAttributeEditor.deleteInstruction(offset); + } + } + + + /** + * Deletes the given branch instruction, or replaces it by a simpler branch + * instruction, if possible. + */ + private void replaceBranchInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); + + // Is there exactly one branch target (not from a goto or jsr)? + if (branchTargets != null && + branchTargets.instructionOffsetCount() == 1) + { + // Is it branching to the next instruction? + int branchOffset = branchTargets.instructionOffset(0) - offset; + if (branchOffset == instruction.length(offset)) + { + if (DEBUG) System.out.println(" Ignoring zero branch instruction at ["+offset+"]"); + } + else + { + // Replace the branch instruction by a simple branch instruction. + Instruction replacementInstruction = + new BranchInstruction(InstructionConstants.OP_GOTO, + branchOffset); + + replaceInstruction(clazz, offset, instruction, replacementInstruction); + } + } + } + + + /** + * Makes sure all branch targets of the given switch instruction are valid. + */ + private void replaceSwitchInstruction(Clazz clazz, + int offset, + SwitchInstruction switchInstruction) + { + // Get the actual branch targets. + InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); + + // Get an offset that can serve as a valid default offset. + int defaultOffset = + branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) - + offset; + + Instruction replacementInstruction = null; + + // Check the jump offsets. + int[] jumpOffsets = switchInstruction.jumpOffsets; + for (int index = 0; index < jumpOffsets.length; index++) + { + if (!branchTargets.contains(offset + jumpOffsets[index])) + { + // Replace the unused offset. + jumpOffsets[index] = defaultOffset; + + // Remember to replace the instruction. + replacementInstruction = switchInstruction; + } + } + + // Check the default offset. + if (!branchTargets.contains(offset + switchInstruction.defaultOffset)) + { + // Replace the unused offset. + switchInstruction.defaultOffset = defaultOffset; + + // Remember to replace the instruction. + replacementInstruction = switchInstruction; + } + + if (replacementInstruction != null) + { + replaceInstruction(clazz, offset, switchInstruction, replacementInstruction); + } + } + + + /** + * Replaces the given instruction by an infinite loop. + */ + private void replaceByInfiniteLoop(Clazz clazz, + int offset, + Instruction instruction) + { + // Replace the instruction by an infinite loop. + Instruction replacementInstruction = + new BranchInstruction(InstructionConstants.OP_GOTO, 0); + + if (DEBUG) System.out.println(" Replacing unreachable instruction by infinite loop "+replacementInstruction.toString(offset)); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + // Note: we're not passing the right arguments for now, knowing that + // they aren't used anyway. + instruction.accept(clazz, null, null, offset, extraInstructionVisitor); + } + } + + + /** + * Replaces the instruction at a given offset by a given push instruction. + */ + private void replaceInstruction(Clazz clazz, + int offset, + Instruction instruction, + Instruction replacementInstruction) + { + // Pop unneeded stack entries if necessary. + int popCount = + instruction.stackPopCount(clazz) - + replacementInstruction.stackPopCount(clazz); + + insertPopInstructions(offset, popCount); + + if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)")); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + // Note: we're not passing the right arguments for now, knowing that + // they aren't used anyway. + instruction.accept(clazz, null, null, offset, extraInstructionVisitor); + } + } + + + /** + * Pops the given number of stack entries before the instruction at the + * given offset. + */ + private void insertPopInstructions(int offset, int popCount) + { + switch (popCount) + { + case 0: + { + break; + } + case 1: + { + // Insert a single pop instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP); + + codeAttributeEditor.insertBeforeInstruction(offset, + popInstruction); + break; + } + case 2: + { + // Insert a single pop2 instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP2); + + codeAttributeEditor.insertBeforeInstruction(offset, + popInstruction); + break; + } + default: + { + // Insert the specified number of pop instructions. + Instruction[] popInstructions = + new Instruction[popCount / 2 + popCount % 2]; + + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP2); + + for (int index = 0; index < popCount / 2; index++) + { + popInstructions[index] = popInstruction; + } + + if (popCount % 2 == 1) + { + popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP); + + popInstructions[popCount / 2] = popInstruction; + } + + codeAttributeEditor.insertBeforeInstruction(offset, + popInstructions); + break; + } + } + } +} diff --git a/src/proguard/optimize/evaluation/LivenessAnalyzer.java b/src/proguard/optimize/evaluation/LivenessAnalyzer.java new file mode 100644 index 000000000..5ce8ccb97 --- /dev/null +++ b/src/proguard/optimize/evaluation/LivenessAnalyzer.java @@ -0,0 +1,526 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +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 instruction at the given offset has ever been + * executed during the partial evaluation. + */ + public boolean isTraced(int instructionOffset) + { + return partialEvaluator.isTraced(instructionOffset); + } + + + /** + * 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 + { + // 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; + } + } + } + + + /** + * Returns the combined liveness mask of the variables right before the + * specified instruction offsets. + */ + 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 000000000..d6baa67cd --- /dev/null +++ b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java @@ -0,0 +1,195 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 +{ + private final boolean loadFieldValues; + private final boolean loadMethodParameterValues; + private final boolean loadMethodReturnValues; + + + /** + * Creates a new LoadingInvocationUnit with the given value factory. + */ + public LoadingInvocationUnit(ValueFactory valueFactory) + { + this(valueFactory, false, false, false); + } + + + /** + * Creates a new LoadingInvocationUnit with the given value factory, for + * loading the specified values. + */ + public LoadingInvocationUnit(ValueFactory valueFactory, + boolean loadFieldValues, + boolean loadMethodParameterValues, + boolean loadMethodReturnValues) + { + super(valueFactory); + + this.loadFieldValues = loadFieldValues; + this.loadMethodParameterValues = loadMethodParameterValues; + this.loadMethodReturnValues = loadMethodReturnValues; + } + + + // Implementations for BasicInvocationUnit. + + protected Value getFieldClassValue(Clazz clazz, + RefConstant refConstant, + String type) + { + if (loadFieldValues) + { + // Do we know this field? + Member referencedMember = refConstant.referencedMember; + if (referencedMember != null) + { + // Retrieve the stored field class value. + ReferenceValue value = StoringInvocationUnit.getFieldClassValue((Field)referencedMember); + if (value != null && + value.isParticular()) + { + return value; + } + } + } + + return super.getFieldClassValue(clazz, refConstant, type); + } + + + protected Value getFieldValue(Clazz clazz, + RefConstant refConstant, + String type) + { + if (loadFieldValues) + { + // Do we know this field? + Member referencedMember = refConstant.referencedMember; + if (referencedMember != null) + { + // Retrieve the stored field value. + Value value = StoringInvocationUnit.getFieldValue((Field)referencedMember); + if (value != null && + value.isParticular()) + { + return value; + } + } + } + + return super.getFieldValue(clazz, refConstant, type); + } + + + protected Value getMethodParameterValue(Clazz clazz, + Method method, + int parameterIndex, + String type, + Clazz referencedClass) + { + if (loadMethodParameterValues) + { + // Retrieve the stored method parameter value. + Value value = StoringInvocationUnit.getMethodParameterValue(method, parameterIndex); + if (value != null && + value.isParticular()) + { + return value; + } + } + + return super.getMethodParameterValue(clazz, + method, + parameterIndex, + type, + referencedClass); + } + + + protected Value getMethodReturnValue(Clazz clazz, + RefConstant refConstant, + String type) + { + if (loadMethodReturnValues) + { + // Do we know this method? + Member referencedMember = refConstant.referencedMember; + if (referencedMember != null) + { + // Retrieve the stored method return value. + Value value = StoringInvocationUnit.getMethodReturnValue((Method)referencedMember); + if (value != null && + value.isParticular()) + { + return value; + } + } + } + + return super.getMethodReturnValue(clazz, + refConstant, + type); + } + + +// // Small utility methods. +// +// private Value refresh(Value value) +// { +// if (value.isParticular()) +// { +// return value; +// } +// +// switch (value.computationalType()) +// { +// case Value.TYPE_INTEGER: return valueFactory.createIntegerValue(); +// case Value.TYPE_LONG: return valueFactory.createLongValue(); +// case Value.TYPE_FLOAT: return valueFactory.createFloatValue(); +// case Value.TYPE_DOUBLE: return valueFactory.createDoubleValue(); +// default: +// { +// ReferenceValue referenceValue = value.referenceValue(); +// +// return valueFactory.createReferenceValue(referenceValue.getType(), +// referenceValue.getReferencedClass(), +// referenceValue.isNull() != Value.NEVER); +// } +// } +// } +} diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java new file mode 100644 index 000000000..6a5bedf6e --- /dev/null +++ b/src/proguard/optimize/evaluation/PartialEvaluator.java @@ -0,0 +1,1275 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.instruction.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.peephole.BranchTargetFinder; + +import java.util.Arrays; + +/** + * This AttributeVisitor performs partial evaluation on the code attributes + * that it visits. + * + * @author Eric Lafortune + */ +public class PartialEvaluator +extends SimplifiedVisitor +implements AttributeVisitor, + ExceptionInfoVisitor +{ + //* + private static final boolean DEBUG = false; + private static final boolean DEBUG_RESULTS = false; + /*/ + private static boolean DEBUG = true; + private static boolean DEBUG_RESULTS = true; + //*/ + + 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; + + private final ValueFactory valueFactory; + private final InvocationUnit invocationUnit; + private final boolean evaluateAllCode; + + 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 boolean evaluateExceptions; + + private final BasicBranchUnit branchUnit; + private final BranchTargetFinder branchTargetFinder; + + private final java.util.Stack callingInstructionBlockStack; + private final java.util.Stack instructionBlockStack = new java.util.Stack(); + + + /** + * Creates a simple PartialEvaluator. + */ + public PartialEvaluator() + { + this(new ValueFactory(), + new BasicInvocationUnit(new ValueFactory()), + true); + } + + + /** + * Creates a new PartialEvaluator. + * @param valueFactory the value factory that will create all values + * during evaluation. + * @param invocationUnit the invocation unit that will handle all + * communication with other fields and methods. + * @param evaluateAllCode a flag that specifies whether all branch targets + * and exception handlers should be evaluated, + * even if they are unreachable. + */ + public PartialEvaluator(ValueFactory valueFactory, + InvocationUnit invocationUnit, + boolean evaluateAllCode) + { + this(valueFactory, + invocationUnit, + evaluateAllCode, + evaluateAllCode ? + new BasicBranchUnit() : + new TracedBranchUnit(), + new BranchTargetFinder(), + null); + } + + + /** + * Creates a new PartialEvaluator, based on an existing one. + * @param partialEvaluator the subroutine calling partial evaluator. + */ + private PartialEvaluator(PartialEvaluator partialEvaluator) + { + this(partialEvaluator.valueFactory, + partialEvaluator.invocationUnit, + partialEvaluator.evaluateAllCode, + partialEvaluator.branchUnit, + partialEvaluator.branchTargetFinder, + partialEvaluator.instructionBlockStack); + } + + + /** + * Creates a new PartialEvaluator. + * @param valueFactory the value factory that will create all + * values during evaluation. + * @param invocationUnit the invocation unit that will handle all + * communication with other fields and methods. + * @param evaluateAllCode a flag that specifies whether all branch + * targets and exception handlers should be + * evaluated, even if they are unreachable. + * @param branchUnit the branch unit that will handle all + * branches. + * @param branchTargetFinder the utility class that will find all + * branches. + */ + private PartialEvaluator(ValueFactory valueFactory, + InvocationUnit invocationUnit, + boolean evaluateAllCode, + BasicBranchUnit branchUnit, + BranchTargetFinder branchTargetFinder, + java.util.Stack callingInstructionBlockStack) + { + this.valueFactory = valueFactory; + this.invocationUnit = invocationUnit; + this.evaluateAllCode = evaluateAllCode; + this.branchUnit = branchUnit; + this.branchTargetFinder = branchTargetFinder; + this.callingInstructionBlockStack = callingInstructionBlockStack == null ? + this.instructionBlockStack : + callingInstructionBlockStack; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { +// DEBUG = DEBUG_RESULTS = +// 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. + 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()); + + System.out.println("Evaluation results:"); + + int offset = 0; + do + { + if (isBranchOrExceptionTarget(offset)) + { + System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); + 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)) + { + int initializationOffset = branchTargetFinder.initializationOffset(offset); + if (initializationOffset != NONE) + { + System.out.println(" is to be initialized at ["+initializationOffset+"]"); + } + + InstructionOffsetValue branchTargets = branchTargets(offset); + if (branchTargets != null) + { + System.out.println(" has overall been branching to "+branchTargets); + } + + System.out.println(" Vars: "+variablesAfter[offset]); + System.out.println(" Stack: "+stacksAfter[offset]); + } + + offset += instruction.length(offset); + } + while (offset < codeAttribute.u4codeLength); + } + + throw ex; + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Evaluate the instructions, starting at the entry point. + 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. + initializeArrays(codeAttribute); + initializeParameters(clazz, method, codeAttribute, variables); + + // Find all instruction offsets,... + codeAttribute.accept(clazz, method, branchTargetFinder); + + // Start executing the first instruction block. + evaluateInstructionBlockAndExceptionHandlers(clazz, + method, + codeAttribute, + variables, + stack, + 0, + codeAttribute.u4codeLength); + + if (DEBUG_RESULTS) + { + System.out.println("Evaluation results:"); + + int offset = 0; + do + { + if (isBranchOrExceptionTarget(offset)) + { + System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); + 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)) + { + int initializationOffset = branchTargetFinder.initializationOffset(offset); + if (initializationOffset != NONE) + { + System.out.println(" is to be initialized at ["+initializationOffset+"]"); + } + + InstructionOffsetValue branchTargets = branchTargets(offset); + if (branchTargets != null) + { + System.out.println(" has overall been branching to "+branchTargets); + } + + System.out.println(" Vars: "+variablesAfter[offset]); + System.out.println(" Stack: "+stacksAfter[offset]); + } + + offset += instruction.length(offset); + } + while (offset < codeAttribute.u4codeLength); + } + } + + + /** + * Returns whether a block of instructions is ever used. + */ + public boolean isTraced(int startOffset, int endOffset) + { + for (int index = startOffset; index < endOffset; index++) + { + if (isTraced(index)) + { + return true; + } + } + + return false; + } + + + /** + * Returns whether the instruction at the given offset has ever been + * executed during the partial evaluation. + */ + public boolean isTraced(int instructionOffset) + { + return evaluationCounts[instructionOffset] > 0; + } + + + /** + * Returns whether there is an instruction at the given offset. + */ + public boolean isInstruction(int instructionOffset) + { + return branchTargetFinder.isInstruction(instructionOffset); + } + + + /** + * Returns whether the instruction at the given offset is the target of a + * branch instruction or an exception. + */ + public boolean isBranchOrExceptionTarget(int instructionOffset) + { + return branchTargetFinder.isBranchTarget(instructionOffset) || + branchTargetFinder.isExceptionHandler(instructionOffset); + } + + + /** + * Returns whether the instruction at the given offset is the start of a + * subroutine. + */ + public boolean isSubroutineStart(int instructionOffset) + { + return branchTargetFinder.isSubroutineStart(instructionOffset); + } + + + /** + * Returns whether the instruction at the given offset is a subroutine + * invocation. + */ + public boolean isSubroutineInvocation(int instructionOffset) + { + return branchTargetFinder.isSubroutineInvocation(instructionOffset); + } + + + /** + * Returns whether the instruction at the given offset is part of a + * subroutine. + */ + public boolean isSubroutine(int instructionOffset) + { + return branchTargetFinder.isSubroutine(instructionOffset); + } + + + /** + * Returns whether the subroutine at the given offset is ever returning + * by means of a regular 'ret' instruction. + */ + public boolean isSubroutineReturning(int instructionOffset) + { + return branchTargetFinder.isSubroutineReturning(instructionOffset); + } + + + /** + * Returns the offset after the subroutine that starts at the given + * offset. + */ + public int subroutineEnd(int instructionOffset) + { + return branchTargetFinder.subroutineEnd(instructionOffset); + } + + + /** + * Returns the instruction offset at which the object instance that is + * created at the given 'new' instruction offset is initialized, or + * NONE if it is not being created. + */ + public int initializationOffset(int instructionOffset) + { + return branchTargetFinder.initializationOffset(instructionOffset); + } + + + /** + * Returns whether the method is an instance initializer. + */ + public boolean isInitializer() + { + return branchTargetFinder.isInitializer(); + } + + + /** + * Returns the instruction offset at which this initializer is calling + * the "super" or "this" initializer method, or NONE if it is + * not an initializer. + */ + public int superInitializationOffset() + { + return branchTargetFinder.superInitializationOffset(); + } + + + /** + * Returns the offset of the 'new' instruction that corresponds to the + * invocation of the instance initializer at the given offset, or + * AT_METHOD_ENTRY if the invocation is calling the "super" or + * "this" initializer method, , or NONE if it is not a 'new' + * instruction. + */ + public int creationOffset(int offset) + { + return branchTargetFinder.creationOffset(offset); + } + + + /** + * Returns the variables before execution of the instruction at the given + * offset. + */ + public TracedVariables getVariablesBefore(int instructionOffset) + { + return variablesBefore[instructionOffset]; + } + + + /** + * Returns the variables after execution of the instruction at the given + * offset. + */ + public TracedVariables getVariablesAfter(int instructionOffset) + { + return variablesAfter[instructionOffset]; + } + + + /** + * Returns the stack before execution of the instruction at the given + * offset. + */ + public TracedStack getStackBefore(int instructionOffset) + { + return stacksBefore[instructionOffset]; + } + + + /** + * Returns the stack after execution of the instruction at the given + * offset. + */ + public TracedStack getStackAfter(int instructionOffset) + { + return stacksAfter[instructionOffset]; + } + + + /** + * Returns the instruction offsets that branch to the given instruction + * offset. + */ + public InstructionOffsetValue branchOrigins(int instructionOffset) + { + return branchOriginValues[instructionOffset]; + } + + + /** + * Returns the instruction offsets to which the given instruction offset + * branches. + */ + public InstructionOffsetValue branchTargets(int instructionOffset) + { + return branchTargetValues[instructionOffset]; + } + + + // Utility methods to evaluate instruction blocks. + + /** + * Pushes block of instructions to be executed in the calling partial + * evaluator. + */ + private void pushCallingInstructionBlock(TracedVariables variables, + TracedStack stack, + int startOffset) + { + callingInstructionBlockStack.push(new MyInstructionBlock(variables, + stack, + startOffset)); + } + + + /** + * Pushes block of instructions to be executed in this partial evaluator. + */ + private void pushInstructionBlock(TracedVariables variables, + TracedStack stack, + int startOffset) + { + instructionBlockStack.push(new MyInstructionBlock(variables, + stack, + startOffset)); + } + + + /** + * Evaluates the instruction block and the exception handlers covering the + * given instruction range in the given code. + */ + private void evaluateInstructionBlockAndExceptionHandlers(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + TracedVariables variables, + TracedStack stack, + int startOffset, + int endOffset) + { + evaluateInstructionBlock(clazz, + method, + codeAttribute, + variables, + stack, + startOffset); + + evaluateExceptionHandlers(clazz, + method, + codeAttribute, + startOffset, + endOffset); + } + + + /** + * Evaluates a block of instructions, starting at the given offset and ending + * at a branch instruction, a return instruction, or a throw instruction. + */ + private void evaluateInstructionBlock(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + TracedVariables variables, + TracedStack stack, + int startOffset) + { + // Execute the initial instruction block. + evaluateSingleInstructionBlock(clazz, + method, + codeAttribute, + variables, + stack, + startOffset); + + // Execute all resulting instruction blocks on the execution stack. + while (!instructionBlockStack.empty()) + { + if (DEBUG) System.out.println("Popping alternative branch out of "+instructionBlockStack.size()+" blocks"); + + MyInstructionBlock instructionBlock = + (MyInstructionBlock)instructionBlockStack.pop(); + + evaluateSingleInstructionBlock(clazz, + method, + codeAttribute, + instructionBlock.variables, + instructionBlock.stack, + instructionBlock.startOffset); + } + } + + + /** + * 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 evaluateSingleInstructionBlock(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + TracedVariables variables, + TracedStack stack, + int startOffset) + { + byte[] code = codeAttribute.code; + + if (DEBUG) + { + System.out.println("Instruction block starting at ["+startOffset+"] in "+ + ClassUtil.externalFullMethodDescription(clazz.getName(), + 0, + method.getName(clazz), + method.getDescriptor(clazz))); + System.out.println("Init vars: "+variables); + System.out.println("Init stack: "+stack); + } + + Processor processor = new Processor(variables, + stack, + valueFactory, + branchUnit, + invocationUnit); + + int instructionOffset = startOffset; + + 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]; + if (evaluationCount == 0) + { + // First time we're passing by this instruction. + if (variablesBefore[instructionOffset] == null) + { + // There's not even a context at this index yet. + variablesBefore[instructionOffset] = new TracedVariables(variables); + stacksBefore[instructionOffset] = new TracedStack(stack); + } + else + { + // Reuse the context objects at this index. + variablesBefore[instructionOffset].initialize(variables); + stacksBefore[instructionOffset].copy(stack); + } + + // We'll execute in the generalized context, because it is + // the same as the current context. + generalizedContexts[instructionOffset] = true; + } + else + { + // Merge in the current context. + boolean variablesChanged = variablesBefore[instructionOffset].generalize(variables, true); + boolean stackChanged = stacksBefore[instructionOffset].generalize(stack); + + //System.out.println("GVars: "+variablesBefore[instructionOffset]); + //System.out.println("GStack: "+stacksBefore[instructionOffset]); + + // Bail out if the current context is the same as last time. + if (!variablesChanged && + !stackChanged && + generalizedContexts[instructionOffset]) + { + if (DEBUG) System.out.println("Repeated variables, stack, and branch targets"); + + break; + } + + // See if this instruction has been evaluated an excessive number + // 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], false); + stack.generalize(stacksBefore[instructionOffset]); + + // We'll execute in the generalized context. + generalizedContexts[instructionOffset] = true; + } + else + { + // We'll execute in the current context. + generalizedContexts[instructionOffset] = false; + } + } + + // 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 = InstructionOffsetValue.EMPTY_VALUE; + + // Note that the instruction is only volatile. + Instruction instruction = InstructionFactory.create(code, instructionOffset); + + // By default, the next instruction will be the one after this + // instruction. + int nextInstructionOffset = instructionOffset + + instruction.length(instructionOffset); + InstructionOffsetValue nextInstructionOffsetValue = new InstructionOffsetValue(nextInstructionOffset); + branchUnit.resetCalled(); + branchUnit.setTraceBranchTargets(nextInstructionOffsetValue); + + if (DEBUG) + { + System.out.println(instruction.toString(instructionOffset)); + } + + try + { + // 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(clazz, + method, + codeAttribute, + instructionOffset, + processor); + } + catch (RuntimeException ex) + { + 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()+")"); + + throw ex; + } + + // Collect the branch targets from the branch unit. + InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets(); + int branchTargetCount = branchTargets.instructionOffsetCount(); + + // Stop tracing. + branchUnit.setTraceBranchTargets(traceValue); + + if (DEBUG) + { + if (branchUnit.wasCalled()) + { + System.out.println(" is branching to "+branchTargets); + } + if (branchTargetValues[instructionOffset] != null) + { + System.out.println(" has up till now been branching to "+branchTargetValues[instructionOffset]); + } + + System.out.println(" Vars: "+variables); + System.out.println(" Stack: "+stack); + } + + // Maintain a generalized local variable frame and stack at this + // instruction offset, after execution. + if (evaluationCount == 0) + { + // First time we're passing by this instruction. + if (variablesAfter[instructionOffset] == null) + { + // There's not even a context at this index yet. + variablesAfter[instructionOffset] = new TracedVariables(variables); + stacksAfter[instructionOffset] = new TracedStack(stack); + } + else + { + // Reuse the context objects at this index. + variablesAfter[instructionOffset].initialize(variables); + stacksAfter[instructionOffset].copy(stack); + } + } + else + { + // Merge in the current context. + variablesAfter[instructionOffset].generalize(variables, true); + stacksAfter[instructionOffset].generalize(stack); + } + + // Did the branch unit get called? + if (branchUnit.wasCalled()) + { + // Accumulate the branch targets at this offset. + branchTargetValues[instructionOffset] = branchTargetValues[instructionOffset] == null ? + branchTargets : + branchTargetValues[instructionOffset].generalize(branchTargets).instructionOffsetValue(); + + // Are there no branch targets at all? + if (branchTargetCount == 0) + { + // Exit from this code block. + break; + } + + // Accumulate the branch origins at the branch target offsets. + InstructionOffsetValue instructionOffsetValue = new InstructionOffsetValue(instructionOffset); + for (int index = 0; index < branchTargetCount; index++) + { + int branchTarget = branchTargets.instructionOffset(index); + branchOriginValues[branchTarget] = branchOriginValues[branchTarget] == null ? + instructionOffsetValue: + branchOriginValues[branchTarget].generalize(instructionOffsetValue).instructionOffsetValue(); + } + + // Are there multiple branch targets? + if (branchTargetCount > 1) + { + // Push them on the execution stack and exit from this block. + for (int index = 0; index < branchTargetCount; index++) + { + if (DEBUG) System.out.println("Pushing alternative branch #"+index+" out of "+branchTargetCount+", from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(index)+"]"); + + pushInstructionBlock(new TracedVariables(variables), + new TracedStack(stack), + branchTargets.instructionOffset(index)); + } + + break; + } + + if (DEBUG) System.out.println("Definite branch from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(0)+"]"); + } + + // Just continue with the next instruction. + instructionOffset = branchTargets.instructionOffset(0); + + // Is this a subroutine invocation? + if (instruction.opcode == InstructionConstants.OP_JSR || + instruction.opcode == InstructionConstants.OP_JSR_W) + { + // Evaluate the subroutine in another partial evaluator. + evaluateSubroutine(clazz, + method, + codeAttribute, + variables, + stack, + instructionOffset, + instructionBlockStack); + + break; + } + else if (instruction.opcode == InstructionConstants.OP_RET) + { + // Let the partial evaluator that has called the subroutine + // handle the evaluation after the return. + pushCallingInstructionBlock(new TracedVariables(variables), + new TracedStack(stack), + instructionOffset); + break; + } + } + + if (DEBUG) System.out.println("Ending processing of instruction block starting at ["+startOffset+"]"); + } + + + /** + * Evaluates a subroutine and its exception handlers, starting at the given + * offset and ending at a subroutine return instruction. + */ + private void evaluateSubroutine(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + TracedVariables variables, + TracedStack stack, + int subroutineStart, + java.util.Stack instructionBlockStack) + { + int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart); + + if (DEBUG) System.out.println("Evaluating subroutine from "+subroutineStart+" to "+subroutineEnd); + + // Create a temporary partial evaluator, so there are no conflicts + // with variables that are alive across subroutine invocations, between + // different invocations. + PartialEvaluator subroutinePartialEvaluator = + new PartialEvaluator(this); + + subroutinePartialEvaluator.initializeArrays(codeAttribute); + + // Evaluate the subroutine. + subroutinePartialEvaluator.evaluateInstructionBlockAndExceptionHandlers(clazz, + method, + codeAttribute, + variables, + stack, + subroutineStart, + subroutineEnd); + + // Merge back the temporary partial evaluator. This way, we'll get + // the lowest common denominator of stacks and variables. + generalize(subroutinePartialEvaluator, 0, codeAttribute.u4codeLength); + + if (DEBUG) System.out.println("Ending subroutine from "+subroutineStart+" to "+subroutineEnd); + } + + + /** + * Generalizes the results of this partial evaluator with those of another + * given partial evaluator, over a given range of instructions. + */ + private void generalize(PartialEvaluator other, + int codeStart, + int codeEnd) + { + if (DEBUG) System.out.println("Generalizing with temporary partial evaluation"); + + for (int offset = codeStart; offset < codeEnd; offset++) + { + if (other.branchOriginValues[offset] != null) + { + branchOriginValues[offset] = branchOriginValues[offset] == null ? + other.branchOriginValues[offset] : + branchOriginValues[offset].generalize(other.branchOriginValues[offset]).instructionOffsetValue(); + } + + if (other.isTraced(offset)) + { + if (other.branchTargetValues[offset] != null) + { + branchTargetValues[offset] = branchTargetValues[offset] == null ? + other.branchTargetValues[offset] : + branchTargetValues[offset].generalize(other.branchTargetValues[offset]).instructionOffsetValue(); + } + + if (evaluationCounts[offset] == 0) + { + variablesBefore[offset] = other.variablesBefore[offset]; + stacksBefore[offset] = other.stacksBefore[offset]; + variablesAfter[offset] = other.variablesAfter[offset]; + stacksAfter[offset] = other.stacksAfter[offset]; + generalizedContexts[offset] = other.generalizedContexts[offset]; + evaluationCounts[offset] = other.evaluationCounts[offset]; + } + else + { + variablesBefore[offset].generalize(other.variablesBefore[offset], false); + stacksBefore[offset] .generalize(other.stacksBefore[offset]); + variablesAfter[offset] .generalize(other.variablesAfter[offset], false); + stacksAfter[offset] .generalize(other.stacksAfter[offset]); + //generalizedContexts[offset] + evaluationCounts[offset] += other.evaluationCounts[offset]; + } + } + } + } + + + /** + * Evaluates the exception handlers covering and targeting the given + * instruction range in the given code. + */ + private void evaluateExceptionHandlers(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int startOffset, + int endOffset) + { + if (DEBUG) System.out.println("Evaluating exceptions covering ["+startOffset+" -> "+endOffset+"]:"); + + ExceptionHandlerFilter exceptionEvaluator = + new ExceptionHandlerFilter(startOffset, + endOffset, + this); + + // Evaluate the exception catch blocks, until their entry variables + // have stabilized. + do + { + // Reset the flag to stop evaluating. + evaluateExceptions = false; + + // Evaluate all relevant exception catch blocks once. + codeAttribute.exceptionsAccept(clazz, + method, + startOffset, + endOffset, + exceptionEvaluator); + } + while (evaluateExceptions); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + int startPC = exceptionInfo.u2startPC; + int endPC = exceptionInfo.u2endPC; + + // Do we have to evaluate this exception catch block? + if (isTraced(startPC, endPC)) + { + int handlerPC = exceptionInfo.u2handlerPC; + int catchType = exceptionInfo.u2catchType; + + if (DEBUG) System.out.println("Evaluating exception ["+startPC +" -> "+endPC +": "+handlerPC+"]:"); + + // Reuse the existing variables and stack objects, ensuring the + // right size. + TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals); + TracedStack stack = new TracedStack(codeAttribute.u2maxStack); + + // Initialize the trace values. + Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY); + variables.setProducerValue(storeValue); + stack.setProducerValue(storeValue); + + // Initialize the variables by generalizing the variables of the + // try block. Make sure to include the results of the last + // instruction for preverification. + generalizeVariables(startPC, + endPC, + evaluateAllCode, + variables); + + // Initialize the the stack. + //stack.push(valueFactory.createReference((ClassConstant)((ProgramClass)clazz).getConstant(exceptionInfo.u2catchType), false)); + String catchClassName = catchType != 0 ? + clazz.getClassName(catchType) : + ClassConstants.INTERNAL_NAME_JAVA_LANG_THROWABLE; + + Clazz catchClass = catchType != 0 ? + ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass : + null; + + stack.push(valueFactory.createReferenceValue(catchClassName, + catchClass, + false)); + + int evaluationCount = evaluationCounts[handlerPC]; + + // Evaluate the instructions, starting at the entry point. + evaluateInstructionBlock(clazz, + method, + codeAttribute, + variables, + stack, + handlerPC); + + // Remember to evaluate all exception handlers once more. + if (!evaluateExceptions) + { + evaluateExceptions = evaluationCount < evaluationCounts[handlerPC]; + } + } +// else if (evaluateAllCode) +// { +// if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"] yet"); +// +// // We don't have any information on the try block yet, but we do +// // have to evaluate the exception handler. +// // Remember to evaluate all exception handlers once more. +// evaluateExceptions = true; +// } + else + { + if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"]"); + } + } + + + // Small utility methods. + + /** + * Initializes the data structures for the variables, stack, etc. + */ + private void initializeArrays(CodeAttribute codeAttribute) + { + int codeLength = codeAttribute.u4codeLength; + + // Create new arrays for storing information at each instruction offset. + if (variablesAfter.length < codeLength) + { + // Create new arrays. + branchOriginValues = new InstructionOffsetValue[codeLength]; + branchTargetValues = new InstructionOffsetValue[codeLength]; + variablesBefore = new TracedVariables[codeLength]; + stacksBefore = new TracedStack[codeLength]; + variablesAfter = new TracedVariables[codeLength]; + stacksAfter = new TracedStack[codeLength]; + generalizedContexts = new boolean[codeLength]; + evaluationCounts = new int[codeLength]; + } + else + { + // Reset the arrays. + Arrays.fill(branchOriginValues, null); + Arrays.fill(branchTargetValues, null); + Arrays.fill(generalizedContexts, false); + Arrays.fill(evaluationCounts, 0); + + for (int index = 0; index < codeLength; index++) + { + if (variablesBefore[index] != null) + { + variablesBefore[index].reset(codeAttribute.u2maxLocals); + } + + if (stacksBefore[index] != null) + { + stacksBefore[index].reset(codeAttribute.u2maxStack); + } + + if (variablesAfter[index] != null) + { + variablesAfter[index].reset(codeAttribute.u2maxLocals); + } + + if (stacksAfter[index] != null) + { + stacksAfter[index].reset(codeAttribute.u2maxStack); + } + } + } + } + + + /** + * Initializes the data structures for the variables, stack, etc. + */ + private void initializeParameters(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + TracedVariables variables) + { + // 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); + + // 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 = new InstructionOffsetValue(AT_METHOD_ENTRY); + + for (int index = 0; index < parameters.size(); index++) + { + variables.setProducerValue(index, atMethodEntry); + } + } + + + /** + * Generalize the local variable frames of a block of instructions. + */ + 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)) + { + 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 000000000..846f6857a --- /dev/null +++ b/src/proguard/optimize/evaluation/StoringInvocationUnit.java @@ -0,0 +1,207 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 +{ + private boolean storeFieldValues; + private boolean storeMethodParameterValues; + private boolean storeMethodReturnValues; + + + /** + * Creates a new StoringInvocationUnit with the given value factory. + */ + public StoringInvocationUnit(ValueFactory valueFactory) + { + this(valueFactory, true, true, true); + } + + + /** + * Creates a new StoringInvocationUnit with the given value factory, for + * storing the specified values. + */ + public StoringInvocationUnit(ValueFactory valueFactory, + boolean storeFieldValues, + boolean storeMethodParameterValues, + boolean storeMethodReturnValues) + { + super(valueFactory); + + this.storeFieldValues = storeFieldValues; + this.storeMethodParameterValues = storeMethodParameterValues; + this.storeMethodReturnValues = storeMethodReturnValues; + } + + + // Implementations for BasicInvocationUnit. + + protected void setFieldClassValue(Clazz clazz, + RefConstant refConstant, + ReferenceValue value) + { + if (storeFieldValues) + { + Member referencedMember = refConstant.referencedMember; + if (referencedMember != null) + { + generalizeFieldClassValue((Field)referencedMember, value); + } + } + } + + + protected void setFieldValue(Clazz clazz, + RefConstant refConstant, + Value value) + { + if (storeFieldValues) + { + Member referencedMember = refConstant.referencedMember; + if (referencedMember != null) + { + generalizeFieldValue((Field)referencedMember, value); + } + } + } + + + protected void setMethodParameterValue(Clazz clazz, + RefConstant refConstant, + int parameterIndex, + Value value) + { + if (storeMethodParameterValues) + { + Member referencedMember = refConstant.referencedMember; + if (referencedMember != null) + { + generalizeMethodParameterValue((Method)referencedMember, + parameterIndex, + value); + } + } + } + + + protected void setMethodReturnValue(Clazz clazz, + Method method, + Value value) + { + if (storeMethodReturnValues) + { + 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 new file mode 100644 index 000000000..e6acf6f2c --- /dev/null +++ b/src/proguard/optimize/evaluation/TracedBranchUnit.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 +extends BasicBranchUnit +{ + // Implementations for BranchUnit. + + public void branchConditionally(Clazz clazz, + CodeAttribute codeAttribute, + int offset, + int branchTarget, + int conditional) + { + if (conditional == Value.ALWAYS) + { + // 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(); + } + } +} diff --git a/src/proguard/optimize/evaluation/VariableOptimizer.java b/src/proguard/optimize/evaluation/VariableOptimizer.java new file mode 100644 index 000000000..73efddc0f --- /dev/null +++ b/src/proguard/optimize/evaluation/VariableOptimizer.java @@ -0,0 +1,357 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.*; +import proguard.classfile.editor.*; +import proguard.classfile.visitor.MemberVisitor; +import proguard.classfile.attribute.*; +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, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor +{ + //* + 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 MemberVisitor extraVariableMemberVisitor; + + private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer(); + private final VariableRemapper variableRemapper = new VariableRemapper(); + private VariableCleaner variableCleaner = new VariableCleaner(); + + private int[] variableMap = new int[ClassConstants.TYPICAL_VARIABLES_SIZE]; + + + /** + * Creates a new VariableOptimizer. + * @param reuseThis specifies whether the 'this' variable can be reused. + * Many JVMs for JME and IBM's JVMs for JSE can't handle + * such reuse. + */ + public VariableOptimizer(boolean reuseThis) + { + this(reuseThis, null); + } + + + /** + * Creates a new VariableOptimizer with an extra visitor. + * @param reuseThis specifies whether the 'this' variable + * can be reused. Many JVMs for JME and + * IBM's JVMs for JSE can't handle such + * reuse. + * @param extraVariableMemberVisitor an optional extra visitor for all + * removed variables. + */ + public VariableOptimizer(boolean reuseThis, + MemberVisitor extraVariableMemberVisitor) + { + this.reuseThis = reuseThis; + this.extraVariableMemberVisitor = extraVariableMemberVisitor; + } + + + // 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); + + // Trim the variables in the local variable tables, because even + // clipping the tables individually may leave some inconsistencies + // between them. + codeAttribute.attributesAccept(clazz, method, this); + + int startIndex = + (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 || + reuseThis ? 0 : 1; + + int parameterSize = + ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), + method.getAccessFlags()); + + 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; + } + } + } + } + + // Have we been able to remap any variables? + if (remapping) + { + if (DEBUG) + { + System.out.println("VariableOptimizer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + for (int index= 0; index < variableSize; index++) + { + System.out.println(" v"+index+" -> "+variableMap[index]); + } + } + + // Remap the variables. + variableRemapper.setVariableMap(variableMap); + variableRemapper.visitCodeAttribute(clazz, method, codeAttribute); + + // Visit the method, if required. + if (extraVariableMemberVisitor != null) + { + method.accept(clazz, extraVariableMemberVisitor); + } + } + else + { + // Just clean up any empty variables. + variableCleaner.visitCodeAttribute(clazz, method, codeAttribute); + } + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Trim the variables in the local variable table. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Trim the variables in the local variable type table. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Trim the local variable to the instructions at which it is alive. + int variable = localVariableInfo.u2index; + int startPC = localVariableInfo.u2startPC; + int endPC = startPC + localVariableInfo.u2length; + + startPC = firstLiveness(startPC, endPC, variable); + endPC = lastLiveness(startPC, endPC, variable); + + // Leave the start address of unused variables unchanged. + int length = endPC - startPC; + if (length > 0) + { + localVariableInfo.u2startPC = startPC; + } + + localVariableInfo.u2length = length; + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Trim the local variable type to the instructions at which it is alive. + int variable = localVariableTypeInfo.u2index; + int startPC = localVariableTypeInfo.u2startPC; + int endPC = startPC + localVariableTypeInfo.u2length; + + startPC = firstLiveness(startPC, endPC, variable); + endPC = lastLiveness(startPC, endPC, variable); + + // Leave the start address of unused variables unchanged. + int length = endPC - startPC; + if (length > 0) + { + localVariableTypeInfo.u2startPC = startPC; + } + + localVariableTypeInfo.u2length = length; + } + + + // 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); + } + } + } + + + /** + * Returns the first instruction offset between the given offsets at which + * the given variable goes alive. + */ + private int firstLiveness(int startOffset, int endOffset, int variableIndex) + { + for (int offset = startOffset; offset < endOffset; offset++) + { + if (livenessAnalyzer.isTraced(offset) && + livenessAnalyzer.isAliveBefore(offset, variableIndex)) + { + return offset; + } + } + + return endOffset; + } + + + /** + * Returns the last instruction offset between the given offsets before + * which the given variable is still alive. + */ + private int lastLiveness(int startOffset, int endOffset, int variableIndex) + { + int previousOffset = endOffset; + + for (int offset = endOffset-1; offset >= startOffset; offset--) + { + if (livenessAnalyzer.isTraced(offset)) + { + if (livenessAnalyzer.isAliveBefore(offset, variableIndex)) + { + return previousOffset; + } + + previousOffset = offset; + } + } + + return endOffset; + } +} diff --git a/src/proguard/optimize/evaluation/package.html b/src/proguard/optimize/evaluation/package.html new file mode 100644 index 000000000..5341f9f8b --- /dev/null +++ b/src/proguard/optimize/evaluation/package.html @@ -0,0 +1,4 @@ + +This package contains visitors that perform partial evaluation and subsequent +optimizations on byte code. + diff --git a/src/proguard/optimize/info/AccessMethodMarker.java b/src/proguard/optimize/info/AccessMethodMarker.java new file mode 100644 index 000000000..e4c8d7c8c --- /dev/null +++ b/src/proguard/optimize/info/AccessMethodMarker.java @@ -0,0 +1,202 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +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 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // Check the bootstrap method. + invokeDynamicConstant.bootstrapMethodHandleAccept(clazz, this); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + // Check the method reference. + clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, 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 000000000..07bfefb6b --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..8f87a08bd --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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/CaughtClassFilter.java b/src/proguard/optimize/info/CaughtClassFilter.java new file mode 100644 index 000000000..762e7de96 --- /dev/null +++ b/src/proguard/optimize/info/CaughtClassFilter.java @@ -0,0 +1,63 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * This ClassVisitor delegates all its method calls to another ClassVisitor, + * but only for Clazz objects that are caught as exceptions. + * + * @see CaughtClassMarker + * @author Eric Lafortune + */ +public class CaughtClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + public CaughtClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (CaughtClassMarker.isCaught(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (CaughtClassMarker.isCaught(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/CaughtClassMarker.java b/src/proguard/optimize/info/CaughtClassMarker.java new file mode 100644 index 000000000..1752f0c0c --- /dev/null +++ b/src/proguard/optimize/info/CaughtClassMarker.java @@ -0,0 +1,64 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassVisitor; + +/** + * This ClassVisitor marks all program classes that it visits as caught. + * This means that these classes are exception classes that occur in exception + * handlers. + * + * @author Eric Lafortune + */ +public class CaughtClassMarker +implements ClassVisitor +{ + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + public void visitProgramClass(ProgramClass programClass) + { + setCaught(programClass); + } + + + // Small utility methods. + + private static void setCaught(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setCaught(); + } + } + + + public static boolean isCaught(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info == null || info.isCaught(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/ClassOptimizationInfo.java b/src/proguard/optimize/info/ClassOptimizationInfo.java new file mode 100644 index 000000000..dbe041ee0 --- /dev/null +++ b/src/proguard/optimize/info/ClassOptimizationInfo.java @@ -0,0 +1,165 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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; + private boolean isCaught = false; + private boolean containsStaticInitializer = false; + private boolean containsPackageVisibleMembers = false; + private boolean invokesPackageVisibleMembers = false; + private Clazz targetClass; + + + 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 void setCaught() + { + isCaught = true; + } + + + public boolean isCaught() + { + return isCaught; + } + + + public void setContainsStaticInitializer() + { + containsStaticInitializer = true; + } + + + public boolean containsStaticInitializer() + { + return containsStaticInitializer; + } + + + public void setContainsPackageVisibleMembers() + { + containsPackageVisibleMembers = true; + } + + + public boolean containsPackageVisibleMembers() + { + return containsPackageVisibleMembers; + } + + + public void setInvokesPackageVisibleMembers() + { + invokesPackageVisibleMembers = true; + } + + + public boolean invokesPackageVisibleMembers() + { + return invokesPackageVisibleMembers; + } + + + public void setTargetClass(Clazz targetClass) + { + this.targetClass = targetClass; + } + + + public Clazz getTargetClass() + { + return targetClass; + } + + + public void merge(ClassOptimizationInfo other) + { + this.isInstantiated |= other.isInstantiated; + this.isInstanceofed |= other.isInstanceofed; + this.isDotClassed |= other.isDotClassed; + this.isCaught |= other.isCaught; + this.containsStaticInitializer |= other.containsStaticInitializer; + this.containsPackageVisibleMembers |= other.containsPackageVisibleMembers; + this.invokesPackageVisibleMembers |= other.invokesPackageVisibleMembers; + } + + + public static void setClassOptimizationInfo(Clazz clazz) + { + clazz.setVisitorInfo(new ClassOptimizationInfo()); + } + + + public static ClassOptimizationInfo getClassOptimizationInfo(Clazz clazz) + { + Object visitorInfo = clazz.getVisitorInfo(); + + return visitorInfo instanceof ClassOptimizationInfo ? + (ClassOptimizationInfo)visitorInfo : + null; + } +} diff --git a/src/proguard/optimize/info/ClassOptimizationInfoSetter.java b/src/proguard/optimize/info/ClassOptimizationInfoSetter.java new file mode 100644 index 000000000..f3d78e258 --- /dev/null +++ b/src/proguard/optimize/info/ClassOptimizationInfoSetter.java @@ -0,0 +1,47 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassVisitor; +import proguard.optimize.KeepMarker; + +/** + * This ClassVisitor attaches a ClassOptimizationInfo instance to every class + * that is not being kept that it visits. + * + * @author Eric Lafortune + */ +public class ClassOptimizationInfoSetter +extends SimplifiedVisitor +implements ClassVisitor +{ + // Implementations for MemberVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (!KeepMarker.isKept(programClass)) + { + ClassOptimizationInfo.setClassOptimizationInfo(programClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/DotClassFilter.java b/src/proguard/optimize/info/DotClassFilter.java new file mode 100644 index 000000000..c3fd87839 --- /dev/null +++ b/src/proguard/optimize/info/DotClassFilter.java @@ -0,0 +1,63 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * This ClassVisitor delegates all its method calls to another ClassVisitor, + * but only for Clazz objects that are used in a .class construct. + * + * @see DotClassMarker + * @author Eric Lafortune + */ +public class DotClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + public DotClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (DotClassMarker.isDotClassed(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (DotClassMarker.isDotClassed(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/DotClassMarker.java b/src/proguard/optimize/info/DotClassMarker.java new file mode 100644 index 000000000..ef5cfd1fe --- /dev/null +++ b/src/proguard/optimize/info/DotClassMarker.java @@ -0,0 +1,96 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassVisitor; + +/** + * This InstructionVisitor marks all classes that are used in a .class + * construct by any of the instructions that it visits. + * + * @author Eric Lafortune + */ +public class DotClassMarker +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + 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) + { + if (constantInstruction.opcode == InstructionConstants.OP_LDC || + constantInstruction.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) + { + classConstant.referencedClassAccept(this); + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + public void visitProgramClass(ProgramClass programClass) + { + setDotClassed(programClass); + } + + + // Small utility methods. + + private static void setDotClassed(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setDotClassed(); + } + } + + + public static boolean isDotClassed(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info == null || info.isDotClassed(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/ExceptionInstructionChecker.java b/src/proguard/optimize/info/ExceptionInstructionChecker.java new file mode 100644 index 000000000..4bfa96fea --- /dev/null +++ b/src/proguard/optimize/info/ExceptionInstructionChecker.java @@ -0,0 +1,193 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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. + // Note that monitorexit can not sensibly throw exceptions, except the + // broken and deprecated asynchronous ThreadDeath. Removing the + // artificial infinite looping exception blocks that recent compilers + // add does not strictly follow the JVM specs, but it does have the + // additional benefit of avoiding a bug in the JVM in JDK 1.1. + switch (opcode) + { + case InstructionConstants.OP_IDIV: + case InstructionConstants.OP_LDIV: + case InstructionConstants.OP_IREM: + case InstructionConstants.OP_LREM: + case InstructionConstants.OP_IALOAD: + case InstructionConstants.OP_LALOAD: + case InstructionConstants.OP_FALOAD: + case InstructionConstants.OP_DALOAD: + case InstructionConstants.OP_AALOAD: + case InstructionConstants.OP_BALOAD: + case InstructionConstants.OP_CALOAD: + case InstructionConstants.OP_SALOAD: + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_AASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + case InstructionConstants.OP_NEWARRAY: + case InstructionConstants.OP_ARRAYLENGTH: + case InstructionConstants.OP_ATHROW: + case InstructionConstants.OP_MONITORENTER: + // 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. + switch (opcode) + { + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_PUTFIELD: + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + case InstructionConstants.OP_NEW: + case InstructionConstants.OP_ANEWARRAY: + case InstructionConstants.OP_CHECKCAST: + case InstructionConstants.OP_INSTANCEOF: + case InstructionConstants.OP_MULTIANEWARRAY: + // These instructions may throw exceptions. + mayThrowExceptions = true; + } + +// case InstructionConstants.OP_INVOKEVIRTUAL: +// case InstructionConstants.OP_INVOKESPECIAL: +// case InstructionConstants.OP_INVOKESTATIC: +// case 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, +// false, +// true, +// true, +// 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 000000000..0fa916717 --- /dev/null +++ b/src/proguard/optimize/info/FieldOptimizationInfo.java @@ -0,0 +1,204 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.*; +import proguard.classfile.attribute.*; +import proguard.classfile.util.*; +import proguard.evaluation.value.*; +import proguard.evaluation.ConstantValueFactory; + +/** + * This class stores some optimization information that can be attached to + * a field. + * + * @author Eric Lafortune + */ +public class FieldOptimizationInfo +extends SimplifiedVisitor +implements AttributeVisitor +{ + private static final SpecificValueFactory VALUE_FACTORY = new SpecificValueFactory(); + private static final ConstantValueFactory CONSTANT_VALUE_FACTORY = new ConstantValueFactory(VALUE_FACTORY); + + private boolean isWritten; + private boolean isRead; + private boolean canBeMadePrivate = true; + private ReferenceValue referencedClass; + private Value value; + + + public FieldOptimizationInfo(Clazz clazz, Field field) + { + int accessFlags = field.getAccessFlags(); + + isWritten = + isRead = (accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0; + + if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + // See if we can initialize the static field with a constant value. + field.accept(clazz, new AllAttributeVisitor(this)); + } + + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 && + value == null) + { + // Otherwise initialize the non-final field with the default value. + value = initialValue(field.getDescriptor(clazz)); + } + } + + + public FieldOptimizationInfo(FieldOptimizationInfo FieldOptimizationInfo) + { + this.isWritten = FieldOptimizationInfo.isWritten; + this.isRead = FieldOptimizationInfo.isRead; + this.canBeMadePrivate = FieldOptimizationInfo.canBeMadePrivate; + this.referencedClass = FieldOptimizationInfo.referencedClass; + this.value = FieldOptimizationInfo.value; + } + + + 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; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + // Retrieve the initial static field value. + value = CONSTANT_VALUE_FACTORY.constantValue(clazz, constantValueAttribute.u2constantValueIndex); + } + + + // 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) + { + field.setVisitorInfo(new FieldOptimizationInfo(clazz, field)); + } + + + public static FieldOptimizationInfo getFieldOptimizationInfo(Field field) + { + Object visitorInfo = field.getVisitorInfo(); + + return visitorInfo instanceof FieldOptimizationInfo ? + (FieldOptimizationInfo)visitorInfo : + null; + } +} diff --git a/src/proguard/optimize/info/InstanceofClassFilter.java b/src/proguard/optimize/info/InstanceofClassFilter.java new file mode 100644 index 000000000..7cd85bc9f --- /dev/null +++ b/src/proguard/optimize/info/InstanceofClassFilter.java @@ -0,0 +1,63 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * This ClassVisitor delegates all its method calls to another ClassVisitor, + * but only for Clazz objects that are used in an 'instanceof' test. + * + * @see InstanceofClassMarker + * @author Eric Lafortune + */ +public class InstanceofClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + public InstanceofClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (InstanceofClassMarker.isInstanceofed(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (InstanceofClassMarker.isInstanceofed(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/InstanceofClassMarker.java b/src/proguard/optimize/info/InstanceofClassMarker.java new file mode 100644 index 000000000..96d5bafe1 --- /dev/null +++ b/src/proguard/optimize/info/InstanceofClassMarker.java @@ -0,0 +1,93 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstant; +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.ClassVisitor; + +/** + * This InstructionVisitor marks all classes that are used in an 'instanceof' + * test by any of the instructions that it visits. + * + * @author Eric Lafortune + */ +public class InstanceofClassMarker +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + 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) + { + if (constantInstruction.opcode == InstructionConstants.OP_INSTANCEOF) + { + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + } + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.referencedClassAccept(this); + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + public void visitProgramClass(ProgramClass programClass) + { + setInstanceofed(programClass); + } + + + // Small utility methods. + + private static void setInstanceofed(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setInstanceofed(); + } + } + + + public static boolean isInstanceofed(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info == null || info.isInstanceofed(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/InstantiationClassFilter.java b/src/proguard/optimize/info/InstantiationClassFilter.java new file mode 100644 index 000000000..a659f064e --- /dev/null +++ b/src/proguard/optimize/info/InstantiationClassFilter.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * This ClassVisitor delegates all its method calls to another ClassVisitor, + * but only for Clazz objects that are instantiated. + * + * @author Eric Lafortune + */ +public class InstantiationClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + public InstantiationClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (InstantiationClassMarker.isInstantiated(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (InstantiationClassMarker.isInstantiated(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/InstantiationClassMarker.java b/src/proguard/optimize/info/InstantiationClassMarker.java new file mode 100644 index 000000000..b4afffdb3 --- /dev/null +++ b/src/proguard/optimize/info/InstantiationClassMarker.java @@ -0,0 +1,93 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ClassConstant; +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.ClassVisitor; + +/** + * This InstructionVisitor marks all classes that are instantiated by any of + * the instructions that it visits. + * + * @author Eric Lafortune + */ +public class InstantiationClassMarker +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + 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) + { + if (constantInstruction.opcode == InstructionConstants.OP_NEW) + { + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + } + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.referencedClassAccept(this); + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + public void visitProgramClass(ProgramClass programClass) + { + setInstantiated(programClass); + } + + + // Small utility methods. + + private static void setInstantiated(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setInstantiated(); + } + } + + + public static boolean isInstantiated(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info == null || info.isInstantiated(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/MemberOptimizationInfoSetter.java b/src/proguard/optimize/info/MemberOptimizationInfoSetter.java new file mode 100644 index 000000000..3c27c930d --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (!KeepMarker.isKept(programField)) + { + FieldOptimizationInfo.setFieldOptimizationInfo(programClass, + programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (!KeepMarker.isKept(programMethod)) + { + MethodOptimizationInfo.setMethodOptimizationInfo(programClass, + programMethod); + } + } +} diff --git a/src/proguard/optimize/info/MethodInvocationMarker.java b/src/proguard/optimize/info/MethodInvocationMarker.java new file mode 100644 index 000000000..afb23366c --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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 000000000..fe754e5b4 --- /dev/null +++ b/src/proguard/optimize/info/MethodOptimizationInfo.java @@ -0,0 +1,302 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 |= 1L << parameterIndex; + } + + + public void setUsedParameters(long usedParameters) + { + this.usedParameters = usedParameters; + } + + + public boolean isParameterUsed(int parameterIndex) + { + return parameterIndex >= 64 || (usedParameters & (1L << 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 void merge(MethodOptimizationInfo other) + { + if (other != null) + { + this.hasNoSideEffects &= other.hasNoSideEffects; + this.hasSideEffects |= other.hasSideEffects; + //this.canBeMadePrivate &= other.canBeMadePrivate; + this.catchesExceptions |= other.catchesExceptions; + this.branchesBackward |= other.branchesBackward; + this.invokesSuperMethods |= other.invokesSuperMethods; + this.accessesPrivateCode |= other.accessesPrivateCode; + this.accessesPackageCode |= other.accessesPackageCode; + this.accessesProtectedCode |= other.accessesProtectedCode; + } + else + { + this.hasNoSideEffects = false; + this.hasSideEffects = true; + //this.canBeMadePrivate = false; + this.catchesExceptions = true; + this.branchesBackward = true; + this.invokesSuperMethods = true; + this.accessesPrivateCode = true; + this.accessesPackageCode = true; + this.accessesProtectedCode = true; + } + } + + + public static void setMethodOptimizationInfo(Clazz clazz, Method method) + { + MethodLinker.lastMember(method).setVisitorInfo(new MethodOptimizationInfo(clazz, method)); + } + + + 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/info/NoSideEffectMethodMarker.java b/src/proguard/optimize/info/NoSideEffectMethodMarker.java new file mode 100644 index 000000000..bf5ce453c --- /dev/null +++ b/src/proguard/optimize/info/NoSideEffectMethodMarker.java @@ -0,0 +1,91 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 marks all methods that it visits as not having any side + * effects. It will make the SideEffectMethodMarker consider them as such + * without further analysis. + * + * @see SideEffectMethodMarker + * @author Eric Lafortune + */ +public class NoSideEffectMethodMarker +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. + public static final Object KEPT_BUT_NO_SIDE_EFFECTS = new Object(); + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz Clazz, Member member) + { + // Ignore any attempts to mark fields. + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + markNoSideEffects(programMethod); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + markNoSideEffects(libraryMethod); + } + + + // Small utility methods. + + private static void markNoSideEffects(Method method) + { + MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); + if (info != null) + { + info.setNoSideEffects(); + } + else + { + MethodLinker.lastMember(method).setVisitorInfo(KEPT_BUT_NO_SIDE_EFFECTS); + } + } + + + public static boolean hasNoSideEffects(Method method) + { + if (MethodLinker.lastVisitorAccepter(method).getVisitorInfo() == KEPT_BUT_NO_SIDE_EFFECTS) + { + return true; + } + + 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 000000000..06f8500df --- /dev/null +++ b/src/proguard/optimize/info/NonPrivateMemberMarker.java @@ -0,0 +1,171 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 method. + programClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, + ClassConstants.INTERNAL_METHOD_TYPE_CLINIT, + this); + + // Explicitly mark the parameterless 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) + { + // The referenced class member, if any, can never be made private, + // even if it's in the same class. + stringConstant.referencedMemberAccept(this); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + Clazz referencedClass = refConstant.referencedClass; + + // Is it referring 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/PackageVisibleMemberContainingClassMarker.java b/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java new file mode 100644 index 000000000..02e1a1882 --- /dev/null +++ b/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java @@ -0,0 +1,85 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +/** + * This ClassVisitor marks all classes that contain package visible members. + * + * @author Eric Lafortune + */ +public class PackageVisibleMemberContainingClassMarker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor +{ + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + // Check the class itself. + if ((clazz.getAccessFlags() & ClassConstants.INTERNAL_ACC_PUBLIC) == 0) + { + setPackageVisibleMembers(clazz); + } + else + { + // Check the members. + clazz.fieldsAccept(this); + clazz.methodsAccept(this); + } + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + if ((member.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_PUBLIC)) == 0) + { + setPackageVisibleMembers(clazz); + } + } + + + // Small utility methods. + + private static void setPackageVisibleMembers(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setContainsPackageVisibleMembers(); + } + } + + + public static boolean containsPackageVisibleMembers(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info == null || info.containsPackageVisibleMembers(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java b/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java new file mode 100644 index 000000000..3148e3d05 --- /dev/null +++ b/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java @@ -0,0 +1,129 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ConstantVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This ConstantVisitor marks all classes that refer to package visible classes + * or class members. + * + * @author Eric Lafortune + */ +public class PackageVisibleMemberInvokingClassMarker +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) + { + // Check the referenced class and class member, if any. + if (stringConstant.referencedClass != clazz) + { + referencingClass = clazz; + + stringConstant.referencedClassAccept(this); + stringConstant.referencedMemberAccept(this); + } + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + // Check the referenced class and class member. + if (refConstant.referencedClass != clazz) + { + referencingClass = clazz; + + refConstant.referencedClassAccept(this); + refConstant.referencedMemberAccept(this); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Check the referenced class. + if (classConstant.referencedClass != clazz) + { + referencingClass = clazz; + + classConstant.referencedClassAccept(this); + } + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + if ((clazz.getAccessFlags() & + ClassConstants.INTERNAL_ACC_PUBLIC) == 0) + { + setInvokesPackageVisibleMembers(referencingClass); + } + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + if ((member.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_PRIVATE)) == 0) + { + setInvokesPackageVisibleMembers(referencingClass); + } + } + + + // Small utility methods. + + private static void setInvokesPackageVisibleMembers(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setInvokesPackageVisibleMembers(); + } + } + + + public static boolean invokesPackageVisibleMembers(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info == null || info.invokesPackageVisibleMembers(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/ParameterUsageMarker.java b/src/proguard/optimize/info/ParameterUsageMarker.java new file mode 100644 index 000000000..a2a264de5 --- /dev/null +++ b/src/proguard/optimize/info/ParameterUsageMarker.java @@ -0,0 +1,285 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.visitor.MemberVisitor; +import proguard.evaluation.value.Value; +import proguard.optimize.evaluation.PartialEvaluator; + +/** + * 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, + AttributeVisitor, + InstructionVisitor +{ + private static final boolean DEBUG = false; + + + private final boolean markThisParameter; + private final boolean markAllParameters; + private final PartialEvaluator partialEvaluator = new PartialEvaluator(); + + + /** + * Creates a new ParameterUsageMarker. + */ + public ParameterUsageMarker() + { + this(false, false); + } + + + /** + * Creates a new ParameterUsageMarker that optionally marks all parameters. + * @param markThisParameter specifies whether all 'this' parameters should + * be marked as being used. + * @param markAllParameters specifies whether all other parameters should + * be marked as being used. + */ + public ParameterUsageMarker(boolean markThisParameter, + boolean markAllParameters) + { + this.markThisParameter = markThisParameter; + this.markAllParameters = markAllParameters; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + int parameterSize = + ClassUtil.internalMethodParameterSize(programMethod.getDescriptor(programClass), + programMethod.getAccessFlags()); + + if (parameterSize > 0) + { + int accessFlags = programMethod.getAccessFlags(); + + // Must we mark the 'this' parameter? + if (markThisParameter && + (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0) + { + // Mark the 'this' parameter. + markParameterUsed(programMethod, 0); + } + + // Must we mark all other parameters? + if (markAllParameters) + { + // Mark all parameters, without the 'this' parameter. + markUsedParameters(programMethod, + (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? + -1L : -2L); + } + + // Is it a native method? + if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) + { + // Mark all parameters. + 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); + } + + // Mark the parameters that are used by the code. + programMethod.attributesAccept(programClass, this); + } + + 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. + markUsedParameters(libraryMethod, -1L); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Evaluate the code. + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + // Mark the parameters that are used by the code. + codeAttribute.instructionsAccept(clazz, method, this); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + if (partialEvaluator.isTraced(offset) && + variableInstruction.isLoad()) + { + int parameterIndex = variableInstruction.variableIndex; + if (parameterIndex < codeAttribute.u2maxLocals) + { + Value producer = + partialEvaluator.getVariablesBefore(offset).getProducerValue(parameterIndex); + if (producer != null && + producer.instructionOffsetValue().contains(PartialEvaluator.AT_METHOD_ENTRY)) + { + // Mark the variable. + markParameterUsed(method, parameterIndex); + + // Account for Category 2 instructions, which take up two entries. + if (variableInstruction.isCategory2()) + { + markParameterUsed(method, parameterIndex + 1); + } + } + } + } + } + + + // 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; + } +} diff --git a/src/proguard/optimize/info/ReadWriteFieldMarker.java b/src/proguard/optimize/info/ReadWriteFieldMarker.java new file mode 100644 index 000000000..6bd4b2f8b --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 = true; + private boolean writing = true; + + + // 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 000000000..91f1f02d9 --- /dev/null +++ b/src/proguard/optimize/info/SideEffectInstructionChecker.java @@ -0,0 +1,307 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +import java.util.*; + +/** + * This class can tell whether an instruction has any side effects outside of + * its method. Return instructions can be included or not. + * + * @see ReadWriteFieldMarker + * @see StaticInitializerContainingClassMarker + * @see NoSideEffectMethodMarker + * @see SideEffectMethodMarker + * @author Eric Lafortune + */ +public class SideEffectInstructionChecker +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + MemberVisitor +{ + private static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null; + + + private final boolean includeReturnInstructions; + private final boolean includeLocalFieldAccess; + + // A return value for the visitor methods. + private Clazz referencingClass; + private boolean hasSideEffects; + + + public SideEffectInstructionChecker(boolean includeReturnInstructions, + boolean includeLocalFieldAccess) + { + this.includeReturnInstructions = includeReturnInstructions; + this.includeLocalFieldAccess = includeLocalFieldAccess; + } + + + /** + * Returns whether the given instruction has side effects outside of its + * method. + */ + 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. + switch (opcode) + { + case InstructionConstants.OP_IALOAD: + case InstructionConstants.OP_LALOAD: + case InstructionConstants.OP_FALOAD: + case InstructionConstants.OP_DALOAD: + case InstructionConstants.OP_AALOAD: + case InstructionConstants.OP_BALOAD: + case InstructionConstants.OP_CALOAD: + case InstructionConstants.OP_SALOAD: + // These instructions strictly taken may cause a side effect + // (NullPointerException, ArrayIndexOutOfBoundsException). + hasSideEffects = OPTIMIZE_CONSERVATIVELY; + break; + + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_AASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + case InstructionConstants.OP_ATHROW : + case InstructionConstants.OP_MONITORENTER: + case InstructionConstants.OP_MONITOREXIT: + // These instructions always cause a side effect. + hasSideEffects = true; + break; + + case InstructionConstants.OP_IRETURN: + case InstructionConstants.OP_LRETURN: + case InstructionConstants.OP_FRETURN: + case InstructionConstants.OP_DRETURN: + case InstructionConstants.OP_ARETURN: + case InstructionConstants.OP_RETURN: + // These instructions may have a side effect. + hasSideEffects = includeReturnInstructions; + break; + } + } + + + 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. + switch (opcode) + { + case InstructionConstants.OP_RET: + // This instruction may have a side effect. + hasSideEffects = includeReturnInstructions; + break; + } + } + + + 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. + switch (opcode) + { + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + // Check if the field is write-only or volatile, or if the + // invoked method is causing any side effects. + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_PUTFIELD: + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + if (OPTIMIZE_CONSERVATIVELY) + { + // These instructions strictly taken may cause a side effect + // (NullPointerException). + hasSideEffects = true; + } + else + { + // Check if the field is write-only or volatile, or if the + // invoked method is causing any side effects. + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + } + break; + + case InstructionConstants.OP_CHECKCAST: + // This instructions strictly taken may cause a side effect + // (ClassCastException). + hasSideEffects = OPTIMIZE_CONSERVATIVELY; + break; + } + } + + + 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. + switch (opcode) + { + case InstructionConstants.OP_JSR: + case InstructionConstants.OP_JSR_W: + hasSideEffects = includeReturnInstructions; + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // We'll have to assume invoking an unknown method has side effects. + hasSideEffects = true; + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + // Pass the referencing class. + referencingClass = clazz; + + // We'll have to assume accessing an unknown field has side effects. + hasSideEffects = true; + + // Check the referenced field, if known. + fieldrefConstant.referencedMemberAccept(this); + } + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + // Pass the referencing class. + referencingClass = clazz; + + // We'll have to assume invoking an unknown method has side effects. + hasSideEffects = true; + + // Check the referenced method, if known. + refConstant.referencedMemberAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + hasSideEffects = + (includeLocalFieldAccess || !programClass.equals(referencingClass)) && + ((ReadWriteFieldMarker.isRead(programField) && + ReadWriteFieldMarker.isWritten(programField)) || + ((programField.getAccessFlags() & ClassConstants.INTERNAL_ACC_VOLATILE) != 0) || + (!programClass.equals(referencingClass) && + !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass)))); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Note that side effects already include synchronization of some + // implementation of the method. + hasSideEffects = + !NoSideEffectMethodMarker.hasNoSideEffects(programMethod) && + (SideEffectMethodMarker.hasSideEffects(programMethod) || + (!programClass.equals(referencingClass) && + !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass)))); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + hasSideEffects = true; + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + hasSideEffects = + !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod); + } + + + /** + * Returns the set of superclasses and interfaces that are initialized. + */ + private Set initializedSuperClasses(Clazz clazz) + { + Set set = new HashSet(); + + // Visit all superclasses and interfaces, collecting the ones that have + // static initializers. + clazz.hierarchyAccept(true, true, true, false, + new StaticInitializerContainingClassFilter( + new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, + ClassConstants.INTERNAL_METHOD_TYPE_CLINIT, + new SideEffectMethodFilter( + new MemberToClassVisitor( + new ClassCollector(set)))))); + + return set; + } +} diff --git a/src/proguard/optimize/info/SideEffectMethodFilter.java b/src/proguard/optimize/info/SideEffectMethodFilter.java new file mode 100644 index 000000000..52e072a05 --- /dev/null +++ b/src/proguard/optimize/info/SideEffectMethodFilter.java @@ -0,0 +1,73 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.MemberVisitor; + +/** + * This MemberVisitor delegates all its method calls to another MemberVisitor, + * but only for Method objects that are marked as having side effects. + * + * @see SideEffectMethodMarker + * + * @author Eric Lafortune + */ +public class SideEffectMethodFilter +implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new SideEffectMethodFilter. + * @param memberVisitor the member visitor to which the visiting will be + * delegated. + */ + public SideEffectMethodFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) {} + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (SideEffectMethodMarker.hasSideEffects(programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (SideEffectMethodMarker.hasSideEffects(libraryMethod)) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/SideEffectMethodMarker.java b/src/proguard/optimize/info/SideEffectMethodMarker.java new file mode 100644 index 000000000..f7953c040 --- /dev/null +++ b/src/proguard/optimize/info/SideEffectMethodMarker.java @@ -0,0 +1,181 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 +{ + // Reusable objects for checking whether instructions have side effects. + private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(false, true); + private final SideEffectInstructionChecker initializerSideEffectInstructionChecker = new SideEffectInstructionChecker(false, 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; + + SideEffectInstructionChecker checker = + method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ? + initializerSideEffectInstructionChecker : + sideEffectInstructionChecker; + + // 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 (checker.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/info/StaticInitializerContainingClassFilter.java b/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java new file mode 100644 index 000000000..36aa39201 --- /dev/null +++ b/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.ClassVisitor; + +/** + * This ClassVisitor delegates all its method calls to another ClassVisitor, + * but only for Clazz objects that are instantiated. + * + * @author Eric Lafortune + */ +public class StaticInitializerContainingClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + public StaticInitializerContainingClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (StaticInitializerContainingClassMarker.containsStaticInitializer(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (StaticInitializerContainingClassMarker.containsStaticInitializer(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java b/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java new file mode 100644 index 000000000..3a7e64206 --- /dev/null +++ b/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; + +/** + * This ClassVisitor marks all classes that contain static initializers. + * + * @author Eric Lafortune + */ +public class StaticInitializerContainingClassMarker +extends SimplifiedVisitor +implements ClassVisitor +{ + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + if (clazz.findMethod(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, + ClassConstants.INTERNAL_METHOD_TYPE_CLINIT) != null) + { + setStaticInitializer(clazz); + } + } + + + // Small utility methods. + + private static void setStaticInitializer(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setContainsStaticInitializer(); + } + } + + + public static boolean containsStaticInitializer(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info == null || info.containsStaticInitializer(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/SuperInvocationMarker.java b/src/proguard/optimize/info/SuperInvocationMarker.java new file mode 100644 index 000000000..37b118ad7 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +/** + * 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 000000000..b189ca944 --- /dev/null +++ b/src/proguard/optimize/info/VariableUsageMarker.java @@ -0,0 +1,96 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import java.util.Arrays; + +/** + * 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 boolean[] variableUsed = new boolean[ClassConstants.TYPICAL_VARIABLES_SIZE]; + + + /** + * 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) + { + // Create a new array. + variableUsed = new boolean[maxLocals]; + } + else + { + // Reset the array. + Arrays.fill(variableUsed, 0, maxLocals, false); + } + + 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 000000000..d16486e86 --- /dev/null +++ b/src/proguard/optimize/info/package.html @@ -0,0 +1,4 @@ + +This package contains classes to collect additional information about classes +and class members, which can then be used for optimization. + diff --git a/src/proguard/optimize/package.html b/src/proguard/optimize/package.html new file mode 100644 index 000000000..3ee135322 --- /dev/null +++ b/src/proguard/optimize/package.html @@ -0,0 +1,4 @@ + +This package contains visitors that assist with various optimizations of byte +code. + diff --git a/src/proguard/optimize/peephole/BranchTargetFinder.java b/src/proguard/optimize/peephole/BranchTargetFinder.java new file mode 100644 index 000000000..79499f14f --- /dev/null +++ b/src/proguard/optimize/peephole/BranchTargetFinder.java @@ -0,0 +1,748 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +import java.util.Arrays; + +/** + * This AttributeVisitor finds all instruction offsets, branch targets, and + * exception targets in the CodeAttribute objects that it visits. + * + * @author Eric Lafortune + */ +public class BranchTargetFinder +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ExceptionInfoVisitor, + ConstantVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("btf") != null; + //*/ + + public static final int NONE = -2; + public static final int AT_METHOD_ENTRY = -1; + + public static final int UNKNOWN = -1; + public static final int NO_SUBROUTINE = -2; + + 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 boolean containsSubroutines; + + private boolean repeat; + private int currentSubroutineStart; + private int[] recentCreationOffsets = new int[MAXIMUM_CREATION_OFFSETS]; + private int recentCreationOffsetIndex; + private boolean isInitializer; + + + /** + * Returns whether there is an instruction at the given offset in the + * CodeAttribute that was visited most recently. + */ + public boolean isInstruction(int offset) + { + return (instructionMarks[offset] & INSTRUCTION) != 0; + } + + + /** + * Returns whether the instruction at the given offset is the target of + * any kind in the CodeAttribute that was visited most recently. + */ + public boolean isTarget(int offset) + { + return offset == 0 || + (instructionMarks[offset] & (BRANCH_TARGET | + EXCEPTION_START | + EXCEPTION_END | + EXCEPTION_HANDLER)) != 0; + } + + + /** + * Returns whether the instruction at the given offset is the origin of a + * branch instruction in the CodeAttribute that was visited most recently. + */ + public boolean isBranchOrigin(int offset) + { + return (instructionMarks[offset] & BRANCH_ORIGIN) != 0; + } + + + /** + * Returns whether the instruction at the given offset is the target of a + * branch instruction in the CodeAttribute that was visited most recently. + */ + public boolean isBranchTarget(int offset) + { + return (instructionMarks[offset] & BRANCH_TARGET) != 0; + } + + + /** + * 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 CodeAttribute that was visited most recently. + */ + public boolean isExceptionStart(int offset) + { + return (instructionMarks[offset] & EXCEPTION_START) != 0; + } + + + /** + * Returns whether the instruction at the given offset is the end of an + * exception try block in the CodeAttribute that was visited most recently. + */ + public boolean isExceptionEnd(int offset) + { + return (instructionMarks[offset] & EXCEPTION_END) != 0; + } + + + /** + * Returns whether the instruction at the given offset is the start of an + * exception catch block in the CodeAttribute that was visited most recently. + */ + public boolean isExceptionHandler(int offset) + { + return (instructionMarks[offset] & EXCEPTION_HANDLER) != 0; + } + + + /** + * Returns whether the instruction at the given offset is 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 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] >= 0; + } + + + /** + * Returns whether the subroutine at the given offset is ever returning + * by means of a regular 'ret' instruction. + */ + public boolean isSubroutineReturning(int offset) + { + return (instructionMarks[offset] & SUBROUTINE_RETURNING) != 0; + } + + + /** + * 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) + { + return subroutineEnds[offset]; + } + + + /** + * 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 + * NONE 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 NONE 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 CodeAttribute that was + * visited most recently. + */ + public boolean isInitializer(int offset) + { + 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 + * AT_METHOD_ENTRY if the invocation is calling the "super" or + * "this" initializer method, , or NONE if it is not a 'new' + * instruction. + */ + public int creationOffset(int offset) + { + return creationOffsets[offset]; + } + + + /** + * Returns whether the method contains subroutines, in the CodeAttribute + * that was visited most recently. + */ + public boolean containsSubroutines() + { + return containsSubroutines; + } + + + // 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"); + + // Make sure there are sufficiently large arrays. + int codeLength = codeAttribute.u4codeLength; + if (subroutineStarts.length < 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. + Arrays.fill(subroutineStarts, 0, codeLength, UNKNOWN); + Arrays.fill(subroutineEnds, 0, codeLength, UNKNOWN); + Arrays.fill(creationOffsets, 0, codeLength, NONE); + Arrays.fill(initializationOffsets, 0, codeLength, NONE); + } + else + { + // Reset the arrays. + Arrays.fill(instructionMarks, 0, codeLength, (short)0); + Arrays.fill(subroutineStarts, 0, codeLength, UNKNOWN); + Arrays.fill(subroutineEnds, 0, codeLength, UNKNOWN); + Arrays.fill(creationOffsets, 0, codeLength, NONE); + Arrays.fill(initializationOffsets, 0, codeLength, NONE); + + instructionMarks[codeLength] = 0; + } + + superInitializationOffset = NONE; + containsSubroutines = false; + + // Iterate until all subroutines have been fully marked. + do + { + repeat = false; + currentSubroutineStart = NO_SUBROUTINE; + 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; + } + + // Mark branch targets by going over all instructions. + codeAttribute.instructionsAccept(clazz, method, this); + } + while (repeat); + + // The end of the code is a branch target sentinel. + instructionMarks[codeLength] = BRANCH_TARGET; + + // Mark branch targets in the exception table. + codeAttribute.exceptionsAccept(clazz, method, this); + + if (containsSubroutines) + { + // Set the subroutine returning flag and the subroutine end at each + // subroutine start. + int previousSubroutineStart = NO_SUBROUTINE; + + for (int offset = 0; offset < codeLength; offset++) + { + if (isInstruction(offset)) + { + int subroutineStart = subroutineStarts[offset]; + + if (subroutineStart >= 0 && + isSubroutineReturning(offset)) + { + instructionMarks[subroutineStart] |= SUBROUTINE_RETURNING; + } + + if (previousSubroutineStart >= 0) + { + subroutineEnds[previousSubroutineStart] = offset; + } + + previousSubroutineStart = subroutineStart; + } + } + + if (previousSubroutineStart >= 0) + { + subroutineEnds[previousSubroutineStart] = codeLength; + } + + // Set the subroutine returning flag and the subroutine end at each + // subroutine instruction, based on the marks at the subroutine + // start. + for (int offset = 0; offset < codeLength; offset++) + { + if (isSubroutine(offset)) + { + int subroutineStart = subroutineStarts[offset]; + + if (isSubroutineReturning(subroutineStart)) + { + instructionMarks[offset] |= SUBROUTINE_RETURNING; + } + + subroutineEnds[offset] = subroutineEnds[subroutineStart]; + } + } + } + + 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' : '-') + + (isAfterBranch(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)+"]" : "") + + (isNew(index) ? " ["+initializationOffset(index)+"] " : " ---- ") + + InstructionFactory.create(codeAttribute.code, index).toString(index)); + } + } + } + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + // Mark the instruction. + instructionMarks[offset] |= INSTRUCTION; + + // Check if this is an instruction of a subroutine. + checkSubroutine(offset); + + 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_ATHROW) + { + // Mark the branch origin. + markBranchOrigin(offset); + + // Mark the next instruction. + markAfterBranchOrigin(offset + simpleInstruction.length(offset)); + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + // Mark the instruction. + instructionMarks[offset] |= INSTRUCTION; + + // Check if this is an instruction of a subroutine. + checkSubroutine(offset); + + // Check if the instruction is a 'new' instruction. + if (constantInstruction.opcode == InstructionConstants.OP_NEW) + { + // 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(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + // Mark the instruction. + instructionMarks[offset] |= INSTRUCTION; + + // Check if this is an instruction of a subroutine. + checkSubroutine(offset); + + if (variableInstruction.opcode == InstructionConstants.OP_RET) + { + // Mark the method. + containsSubroutines = true; + + // Mark the branch origin. + markBranchOrigin(offset); + + // Mark the subroutine return at its return instruction. + instructionMarks[offset] |= SUBROUTINE_RETURNING; + + // Mark the next instruction. + markAfterBranchOrigin(offset + variableInstruction.length(offset)); + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + int branchOffset = branchInstruction.branchOffset; + int targetOffset = offset + branchOffset; + + // Mark the branch origin. + markBranchOrigin(offset); + + // Check if this is an instruction of a subroutine. + checkSubroutine(offset); + + // Mark the branch target. + markBranchTarget(offset, branchOffset); + + byte opcode = branchInstruction.opcode; + if (opcode == InstructionConstants.OP_JSR || + opcode == InstructionConstants.OP_JSR_W) + { + // Mark the method. + containsSubroutines = true; + + // Mark the subroutine invocation. + instructionMarks[offset] |= SUBROUTINE_INVOCATION; + + // Mark the new subroutine start. + markBranchSubroutineStart(offset, branchOffset, targetOffset); + } + else if (currentSubroutineStart != UNKNOWN) + { + // Mark the continued subroutine start. + markBranchSubroutineStart(offset, branchOffset, currentSubroutineStart); + } + + if (opcode == InstructionConstants.OP_GOTO || + opcode == InstructionConstants.OP_GOTO_W) + { + // Mark the next instruction. + markAfterBranchOrigin(offset + branchInstruction.length(offset)); + } + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + // Mark the branch origin. + markBranchOrigin(offset); + + // Check if this is an instruction of a subroutine. + checkSubroutine(offset); + + // Mark the branch targets of the default jump offset. + markBranch(offset, switchInstruction.defaultOffset); + + // Mark the branch targets of the jump offsets. + markBranches(offset, switchInstruction.jumpOffsets); + + // Mark the next instruction. + markAfterBranchOrigin(offset + switchInstruction.length(offset)); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + isInitializer = methodrefConstant.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + // Mark the exception offsets. + instructionMarks[exceptionInfo.u2startPC] |= EXCEPTION_START; + instructionMarks[exceptionInfo.u2endPC] |= EXCEPTION_END; + instructionMarks[exceptionInfo.u2handlerPC] |= EXCEPTION_HANDLER; + } + + + // Small utility methods. + + /** + * Marks the branch targets and their subroutine starts at the given + * offsets. + */ + private void markBranches(int offset, int[] jumpOffsets) + { + for (int index = 0; index < jumpOffsets.length; index++) + { + markBranch(offset, jumpOffsets[index]); + } + } + + + /** + * Marks the branch target and its subroutine start at the given offset. + */ + private void markBranch(int offset, int jumpOffset) + { + markBranchTarget(offset, jumpOffset); + + if (currentSubroutineStart != UNKNOWN) + { + markBranchSubroutineStart(offset, jumpOffset, currentSubroutineStart); + } + } + + /** + * 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) + { + int targetOffset = offset + jumpOffset; + + instructionMarks[targetOffset] |= BRANCH_TARGET; + } + + + /** + * Marks the subroutine start at the given offset, if applicable. + */ + private void markBranchSubroutineStart(int offset, + int jumpOffset, + int subroutineStart) + { + int targetOffset = offset + jumpOffset; + + // Are we marking a subroutine and branching to an offset that hasn't + // been marked yet? + if (subroutineStarts[targetOffset] == UNKNOWN) + { + // Is it a backward branch? + if (jumpOffset < 0) + { + // Remember the smallest subroutine start. + if (subroutineStart > targetOffset) + { + subroutineStart = targetOffset; + } + + // We'll have to go over all instructions again. + repeat = true; + } + + // Mark the subroutine start of the target. + subroutineStarts[targetOffset] = subroutineStart; + } + } + + + /** + * Marks the instruction at the given offset, after a branch. + */ + private void markAfterBranchOrigin(int nextOffset) + { + instructionMarks[nextOffset] |= AFTER_BRANCH; + + // Stop marking a subroutine. + currentSubroutineStart = UNKNOWN; + } + + + /** + * Checks if the specified instruction is inside a subroutine. + */ + private void checkSubroutine(int offset) + { + // Are we inside a previously marked subroutine? + if (subroutineStarts[offset] != UNKNOWN) + { + // Start marking a subroutine. + currentSubroutineStart = subroutineStarts[offset]; + } + + // Are we marking a subroutine? + else if (currentSubroutineStart != UNKNOWN) + { + // Mark the subroutine start. + subroutineStarts[offset] = currentSubroutineStart; + + if (currentSubroutineStart >= 0) + { + // Mark the subroutine end at the subroutine start. + subroutineEnds[currentSubroutineStart] = offset; + } + } + } +} diff --git a/src/proguard/optimize/peephole/ClassFinalizer.java b/src/proguard/optimize/peephole/ClassFinalizer.java new file mode 100644 index 000000000..378f972ea --- /dev/null +++ b/src/proguard/optimize/peephole/ClassFinalizer.java @@ -0,0 +1,84 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 ClassVisitor makes the program classes that it visits + * final, if possible. + * + * @author Eric Lafortune + */ +public class ClassFinalizer +extends SimplifiedVisitor +implements ClassVisitor +{ + private final ClassVisitor extraClassVisitor; + + + /** + * Creates a new ClassFinalizer. + */ + public ClassFinalizer() + { + this(null); + } + + + /** + * Creates a new ClassFinalizer. + * @param extraClassVisitor an optional extra visitor for all finalized + * classes. + */ + public ClassFinalizer(ClassVisitor extraClassVisitor) + { + this.extraClassVisitor = extraClassVisitor; + } + + + // 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); + } + } + } +} diff --git a/src/proguard/optimize/peephole/ClassMerger.java b/src/proguard/optimize/peephole/ClassMerger.java new file mode 100644 index 000000000..aa40c75b2 --- /dev/null +++ b/src/proguard/optimize/peephole/ClassMerger.java @@ -0,0 +1,641 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeNameFilter; +import proguard.classfile.constant.visitor.*; +import proguard.classfile.editor.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.optimize.KeepMarker; +import proguard.optimize.info.*; +import proguard.util.*; + +import java.util.*; + +/** + * This ClassVisitor inlines the classes that it visits in a given target class, + * whenever possible. + * + * @see RetargetedInnerClassAttributeRemover + * @see TargetClassChanger + * @see ClassReferenceFixer + * @see MemberReferenceFixer + * @see AccessFixer + * @author Eric Lafortune + */ +public class ClassMerger +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("cm") != null; + //*/ + + + private final ProgramClass targetClass; + private final boolean allowAccessModification; + private final boolean mergeInterfacesAggressively; + private final ClassVisitor extraClassVisitor; + + private final MemberVisitor fieldOptimizationInfoCopier = new FieldOptimizationInfoCopier(); + + + /** + * Creates a new ClassMerger that will merge classes into the given target + * class. + * @param targetClass the class into which all visited + * classes will be merged. + * @param allowAccessModification specifies whether the access modifiers + * of classes can be changed in order to + * merge them. + * @param mergeInterfacesAggressively specifies whether interfaces may + * be merged aggressively. + */ + public ClassMerger(ProgramClass targetClass, + boolean allowAccessModification, + boolean mergeInterfacesAggressively) + { + this(targetClass, allowAccessModification, mergeInterfacesAggressively, null); + } + + + /** + * Creates a new ClassMerger that will merge classes into the given target + * class. + * @param targetClass the class into which all visited + * classes will be merged. + * @param allowAccessModification specifies whether the access modifiers + * of classes can be changed in order to + * merge them. + * @param mergeInterfacesAggressively specifies whether interfaces may + * be merged aggressively. + * @param extraClassVisitor an optional extra visitor for all + * merged classes. + */ + public ClassMerger(ProgramClass targetClass, + boolean allowAccessModification, + boolean mergeInterfacesAggressively, + ClassVisitor extraClassVisitor) + { + this.targetClass = targetClass; + this.allowAccessModification = allowAccessModification; + this.mergeInterfacesAggressively = mergeInterfacesAggressively; + this.extraClassVisitor = extraClassVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + //final String CLASS_NAME = "abc/Def"; + //DEBUG = programClass.getName().equals(CLASS_NAME) || + // targetClass.getName().equals(CLASS_NAME); + + // TODO: Remove this when the class merger has stabilized. + // Catch any unexpected exceptions from the actual visiting method. + try + { + visitProgramClass0(programClass); + } + catch (RuntimeException ex) + { + System.err.println("Unexpected error while merging classes:"); + System.err.println(" Class = ["+programClass.getName()+"]"); + System.err.println(" Target class = ["+targetClass.getName()+"]"); + System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + + if (DEBUG) + { + programClass.accept(new ClassPrinter()); + targetClass.accept(new ClassPrinter()); + } + + throw ex; + } + } + + public void visitProgramClass0(ProgramClass programClass) + { + if (!programClass.equals(targetClass) && + + // Don't merge classes that must be preserved. + !KeepMarker.isKept(programClass) && + !KeepMarker.isKept(targetClass) && + + // Only merge classes that haven't been retargeted yet. + getTargetClass(programClass) == null && + getTargetClass(targetClass) == null && + + // Don't merge annotation classes, with all their introspection and + // infinite recursion. + (programClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_ANNOTATTION) == 0 && + + // Only merge classes if we can change the access permissions, or + // if they are in the same package, or + // if they are public and don't contain or invoke package visible + // class members. + (allowAccessModification || + ((programClass.getAccessFlags() & + targetClass.getAccessFlags() & + ClassConstants.INTERNAL_ACC_PUBLIC) != 0 && + !PackageVisibleMemberContainingClassMarker.containsPackageVisibleMembers(programClass) && + !PackageVisibleMemberInvokingClassMarker.invokesPackageVisibleMembers(programClass)) || + ClassUtil.internalPackageName(programClass.getName()).equals( + ClassUtil.internalPackageName(targetClass.getName()))) && + + // Only merge two classes or two interfaces or two abstract classes, + // or a class into an interface with a single implementation. + ((programClass.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_INTERFACE | + ClassConstants.INTERNAL_ACC_ABSTRACT)) == + (targetClass.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_INTERFACE | + ClassConstants.INTERNAL_ACC_ABSTRACT)) || + (isOnlySubClass(programClass, targetClass) && + (programClass.getSuperClass().equals(targetClass) || + programClass.getSuperClass().equals(targetClass.getSuperClass())))) && + + // One class must not implement the other class indirectly. + !indirectlyImplementedInterfaces(programClass).contains(targetClass) && + !targetClass.extendsOrImplements(programClass) && + + // The two classes must have the same superclasses and interfaces + // with static initializers. + initializedSuperClasses(programClass).equals(initializedSuperClasses(targetClass)) && + + // The two classes must have the same superclasses and interfaces + // that are tested with 'instanceof'. + instanceofedSuperClasses(programClass).equals(instanceofedSuperClasses(targetClass)) && + + // The two classes must have the same superclasses that are caught + // as exceptions. + caughtSuperClasses(programClass).equals(caughtSuperClasses(targetClass)) && + + // The two classes must not both be part of a .class construct. + !(DotClassMarker.isDotClassed(programClass) && + DotClassMarker.isDotClassed(targetClass)) && + + // The classes must not have clashing fields. + !haveAnyIdenticalFields(programClass, targetClass) && + + // The two classes must not introduce any unwanted fields. + !introducesUnwantedFields(programClass, targetClass) && + !introducesUnwantedFields(targetClass, programClass) && + + // The two classes must not shadow each others fields. + !shadowsAnyFields(programClass, targetClass) && + !shadowsAnyFields(targetClass, programClass) && + + // The classes must not have clashing methods. + !haveAnyIdenticalMethods(programClass, targetClass) && + + // The classes must not introduce abstract methods, unless + // explicitly allowed. + (mergeInterfacesAggressively || + (!introducesUnwantedAbstractMethods(programClass, targetClass) && + !introducesUnwantedAbstractMethods(targetClass, programClass))) && + + // The classes must not override each others concrete methods. + !overridesAnyMethods(programClass, targetClass) && + !overridesAnyMethods(targetClass, programClass) && + + // The classes must not shadow each others non-private methods. + !shadowsAnyMethods(programClass, targetClass) && + !shadowsAnyMethods(targetClass, programClass)) + { + if (DEBUG) + { + System.out.println("ClassMerger ["+programClass.getName()+"] -> ["+targetClass.getName()+"]"); + System.out.println(" Source interface? ["+((programClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE)!=0)+"]"); + System.out.println(" Target interface? ["+((targetClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE)!=0)+"]"); + System.out.println(" Source subclasses ["+programClass.subClasses+"]"); + System.out.println(" Target subclasses ["+targetClass.subClasses+"]"); + System.out.println(" Source superclass ["+programClass.getSuperClass().getName()+"]"); + System.out.println(" Target superclass ["+targetClass.getSuperClass().getName()+"]"); + + //System.out.println("=== Before ==="); + //programClass.accept(new ClassPrinter()); + //targetClass.accept(new ClassPrinter()); + } + + // Combine the access flags. + int targetAccessFlags = targetClass.getAccessFlags(); + int sourceAccessFlags = programClass.getAccessFlags(); + + targetClass.u2accessFlags = + ((targetAccessFlags & + sourceAccessFlags) & + (ClassConstants.INTERNAL_ACC_INTERFACE | + ClassConstants.INTERNAL_ACC_ABSTRACT)) | + ((targetAccessFlags | + sourceAccessFlags) & + (ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_SUPER | + ClassConstants.INTERNAL_ACC_ANNOTATTION | + ClassConstants.INTERNAL_ACC_ENUM)); + + // Copy over the superclass, unless it's the target class itself. + //if (!targetClass.getName().equals(programClass.getSuperName())) + //{ + // targetClass.u2superClass = + // new ConstantAdder(targetClass).addConstant(programClass, programClass.u2superClass); + //} + + // Copy over the interfaces that aren't present yet and that + // wouldn't cause loops in the class hierarchy. + programClass.interfaceConstantsAccept( + new ExceptClassConstantFilter(targetClass.getName(), + new ImplementedClassConstantFilter(targetClass, + new ImplementingClassConstantFilter(targetClass, + new InterfaceAdder(targetClass))))); + + // Copy over the class members. + MemberAdder memberAdder = + new MemberAdder(targetClass, fieldOptimizationInfoCopier); + + programClass.fieldsAccept(memberAdder); + programClass.methodsAccept(memberAdder); + + // Copy over the other attributes. + programClass.attributesAccept( + new AttributeNameFilter(new NotMatcher(new OrMatcher(new OrMatcher( + new FixedStringMatcher(ClassConstants.ATTR_SourceFile), + new FixedStringMatcher(ClassConstants.ATTR_InnerClasses)), + new FixedStringMatcher(ClassConstants.ATTR_EnclosingMethod))), + new AttributeAdder(targetClass, true))); + + // Update the optimization information of the target class. + ClassOptimizationInfo info = + ClassOptimizationInfo.getClassOptimizationInfo(targetClass); + if (info != null) + { + info.merge(ClassOptimizationInfo.getClassOptimizationInfo(programClass)); + } + + // Remember to replace the inlined class by the target class. + setTargetClass(programClass, targetClass); + + //if (DEBUG) + //{ + // System.out.println("=== After ===="); + // targetClass.accept(new ClassPrinter()); + //} + + // Visit the merged class, if required. + if (extraClassVisitor != null) + { + extraClassVisitor.visitProgramClass(programClass); + } + } + } + + + // Small utility methods. + + /** + * Returns whether a given class is the only subclass of another given class. + */ + private boolean isOnlySubClass(Clazz subClass, + ProgramClass clazz) + { + // TODO: The list of subclasses is not up to date. + return clazz.subClasses != null && + clazz.subClasses.length == 1 && + clazz.subClasses[0].equals(subClass); + } + + + /** + * Returns the set of indirectly implemented interfaces. + */ + private Set indirectlyImplementedInterfaces(Clazz clazz) + { + Set set = new HashSet(); + + ReferencedClassVisitor referencedInterfaceCollector = + new ReferencedClassVisitor( + new ClassHierarchyTraveler(false, false, true, false, + new ClassCollector(set))); + + // Visit all superclasses and collect their interfaces. + clazz.superClassConstantAccept(referencedInterfaceCollector); + + // Visit all interfaces and collect their interfaces. + clazz.interfaceConstantsAccept(referencedInterfaceCollector); + + return set; + } + + + /** + * Returns the set of superclasses and interfaces that are initialized. + */ + private Set initializedSuperClasses(Clazz clazz) + { + Set set = new HashSet(); + + // Visit all superclasses and interfaces, collecting the ones that have + // static initializers. + clazz.hierarchyAccept(true, true, true, false, + new StaticInitializerContainingClassFilter( + new ClassCollector(set))); + + return set; + } + + + /** + * Returns the set of superclasses and interfaces that are used in + * 'instanceof' tests. + */ + private Set instanceofedSuperClasses(Clazz clazz) + { + Set set = new HashSet(); + + // Visit all superclasses and interfaces, collecting the ones that are + // used in an 'instanceof' test. + clazz.hierarchyAccept(true, true, true, false, + new InstanceofClassFilter( + new ClassCollector(set))); + + return set; + } + + + /** + * Returns the set of superclasses that are caught as exceptions. + */ + private Set caughtSuperClasses(Clazz clazz) + { + // Don't bother if this isn't an exception at all. + if (!clazz.extends_(ClassConstants.INTERNAL_NAME_JAVA_LANG_THROWABLE)) + { + return Collections.EMPTY_SET; + } + + // Visit all superclasses, collecting the ones that are caught + // (plus java.lang.Object, in the current implementation). + Set set = new HashSet(); + + clazz.hierarchyAccept(true, true, false, false, + new CaughtClassFilter( + new ClassCollector(set))); + + return set; + } + + + /** + * Returns whether the two given classes have class members with the same + * name and descriptor. + */ + private boolean haveAnyIdenticalFields(Clazz clazz, Clazz targetClass) + { + MemberCounter counter = new MemberCounter(); + + // Visit all fields, counting the with the same name and descriptor in + // the target class. + clazz.fieldsAccept(new SimilarMemberVisitor(targetClass, true, false, false, false, + counter)); + + return counter.getCount() > 0; + } + + + /** + * Returns whether the given class would introduce any unwanted fields + * in the target class. + */ + private boolean introducesUnwantedFields(ProgramClass programClass, + ProgramClass targetClass) + { + // It's ok if the target class is never instantiated, without any other + // subclasses except for maybe the source class. + if (!InstantiationClassMarker.isInstantiated(targetClass) && + (targetClass.subClasses == null || + isOnlySubClass(programClass, targetClass))) + { + return false; + } + + MemberCounter counter = new MemberCounter(); + + // Count all non-static fields in the the source class. + programClass.fieldsAccept(new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC, + counter)); + + return counter.getCount() > 0; + } + + + /** + * Returns whether the given class or its subclasses shadow any fields in + * the given target class. + */ + private boolean shadowsAnyFields(Clazz clazz, Clazz targetClass) + { + MemberCounter counter = new MemberCounter(); + + // Visit all fields, counting the ones that are shadowing non-private + // fields in the class hierarchy of the target class. + clazz.hierarchyAccept(true, false, false, true, + new AllFieldVisitor( + new SimilarMemberVisitor(targetClass, true, true, true, false, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + counter)))); + + return counter.getCount() > 0; + } + + + /** + * Returns whether the two given classes have class members with the same + * name and descriptor. + */ + private boolean haveAnyIdenticalMethods(Clazz clazz, Clazz targetClass) + { + MemberCounter counter = new MemberCounter(); + + // Visit all non-abstract methods, counting the ones that are also + // present in the target class. + clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_ABSTRACT, + new SimilarMemberVisitor(targetClass, true, false, false, false, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_ABSTRACT, + counter)))); + + return counter.getCount() > 0; + } + + + /** + * Returns whether the given class would introduce any abstract methods + * in the target class. + */ + private boolean introducesUnwantedAbstractMethods(Clazz clazz, + ProgramClass targetClass) + { + // It's ok if the target class is already abstract and it has at most + // the class as a subclass. + if ((targetClass.getAccessFlags() & + (ClassConstants.INTERNAL_ACC_ABSTRACT | + ClassConstants.INTERNAL_ACC_INTERFACE)) != 0 && + (targetClass.subClasses == null || + isOnlySubClass(clazz, targetClass))) + { + return false; + } + + MemberCounter counter = new MemberCounter(); + Set targetSet = new HashSet(); + + // Collect all abstract methods, and similar abstract methods in the + // class hierarchy of the target class. + clazz.methodsAccept(new MemberAccessFilter(ClassConstants.INTERNAL_ACC_ABSTRACT, 0, + new MultiMemberVisitor(new MemberVisitor[] + { + counter, + new SimilarMemberVisitor(targetClass, true, true, true, false, + new MemberAccessFilter(ClassConstants.INTERNAL_ACC_ABSTRACT, 0, + new MemberCollector(targetSet))) + }))); + + return targetSet.size() < counter.getCount(); + } + + + /** + * Returns whether the given class overrides any methods in the given + * target class. + */ + private boolean overridesAnyMethods(Clazz clazz, Clazz targetClass) + { + MemberCounter counter = new MemberCounter(); + + // Visit all non-private non-static methods, counting the ones that are + // being overridden in the class hierarchy of the target class. + clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC | ClassConstants.INTERNAL_ACC_ABSTRACT, + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_CLINIT)), + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_INIT)), + new SimilarMemberVisitor(targetClass, true, true, false, false, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC | ClassConstants.INTERNAL_ACC_ABSTRACT, + counter)))))); + + return counter.getCount() > 0; + } + + + /** + * Returns whether the given class or its subclasses shadow any methods in + * the given target class. + */ + private boolean shadowsAnyMethods(Clazz clazz, Clazz targetClass) + { + MemberCounter counter = new MemberCounter(); + + // Visit all private methods, counting the ones that are shadowing + // non-private methods in the class hierarchy of the target class. + clazz.hierarchyAccept(true, false, false, true, + new AllMethodVisitor( + new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0, + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_INIT)), + new SimilarMemberVisitor(targetClass, true, true, true, false, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + counter)))))); + + // Visit all static methods, counting the ones that are shadowing + // non-private methods in the class hierarchy of the target class. + clazz.hierarchyAccept(true, false, false, true, + new AllMethodVisitor( + new MemberAccessFilter(ClassConstants.INTERNAL_ACC_STATIC, 0, + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_CLINIT)), + new SimilarMemberVisitor(targetClass, true, true, true, false, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + counter)))))); + + return counter.getCount() > 0; + } + + + public static void setTargetClass(Clazz clazz, Clazz targetClass) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setTargetClass(targetClass); + } + } + + + public static Clazz getTargetClass(Clazz clazz) + { + Clazz targetClass = null; + + // Return the last target class, if any. + while (true) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info == null) + { + return targetClass; + } + + clazz = info.getTargetClass(); + if (clazz == null) + { + return targetClass; + } + + targetClass = clazz; + } + } + + + /** + * This MemberVisitor copies field optimization info from copied fields. + */ + private static class FieldOptimizationInfoCopier + extends SimplifiedVisitor + implements MemberVisitor + { + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Copy the optimization info from the field that was just copied. + ProgramField copiedField = (ProgramField)programField.getVisitorInfo(); + Object info = copiedField.getVisitorInfo(); + + programField.setVisitorInfo(info instanceof FieldOptimizationInfo ? + new FieldOptimizationInfo((FieldOptimizationInfo)info) : + info); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Linked methods share their optimization info. + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java new file mode 100644 index 000000000..3bfd98cad --- /dev/null +++ b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java @@ -0,0 +1,263 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * 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 +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + + private final InstructionVisitor extraInstructionVisitor; + + private final BranchTargetFinder branchTargetFinder = new BranchTargetFinder(); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, false); + + + /** + * Creates a new GotoCommonCodeReplacer. + * @param extraInstructionVisitor an optional extra visitor for all replaced + * goto instructions. + */ + public GotoCommonCodeReplacer(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"); + + // 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 visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + 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)) + { + int branchOffset = branchInstruction.branchOffset; + int targetOffset = offset + branchOffset; + + // Get the number of common bytes. + int commonCount = commonByteCodeCount(codeAttribute, offset, targetOffset); + + if (commonCount > 0 && + !exceptionBoundary(codeAttribute, offset, targetOffset)) + { + if (DEBUG) + { + System.out.println("GotoCommonCodeReplacer: "+clazz.getName()+"."+method.getName(clazz)+" (["+(offset-commonCount)+"] - "+branchInstruction.toString(offset)+" -> "+targetOffset+")"); + } + + // Delete the common instructions. + for (int delta = 0; delta <= commonCount; delta++) + { + int deleteOffset = offset - delta; + if (branchTargetFinder.isInstruction(deleteOffset)) + { + codeAttributeEditor.clearModifications(deleteOffset); + codeAttributeEditor.deleteInstruction(deleteOffset); + } + } + + // Redirect the goto instruction, if it is still necessary. + int newBranchOffset = branchOffset - commonCount; + if (newBranchOffset != branchInstruction.length(offset)) + { + Instruction newGotoInstruction = + new BranchInstruction(opcode, newBranchOffset).shrink(); + codeAttributeEditor.replaceInstruction(offset, + newGotoInstruction); + } + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + extraInstructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + } + } + } + + + // Small utility methods. + + /** + * Returns the number of common bytes preceding the given offsets, + * avoiding branches and exception blocks. + */ + private int commonByteCodeCount(CodeAttribute codeAttribute, int offset1, int offset2) + { + // Find the block of common instructions preceding it. + byte[] code = codeAttribute.code; + + int successfulDelta = 0; + + for (int delta = 1; + delta <= offset1 && + delta <= offset2 && + offset2 - delta != offset1; + delta++) + { + int newOffset1 = offset1 - delta; + int newOffset2 = offset2 - delta; + + // Is the code identical at both offsets? + if (code[newOffset1] != code[newOffset2]) + { + break; + } + + // Are there instructions at either offset but not both? + if (branchTargetFinder.isInstruction(newOffset1) ^ + branchTargetFinder.isInstruction(newOffset2)) + { + break; + } + + // Are there instructions at both offsets? + if (branchTargetFinder.isInstruction(newOffset1) && + branchTargetFinder.isInstruction(newOffset2)) + { + // Are the offsets involved in some branches? + // Note that the preverifier doesn't like initializer + // invocations to be moved around. + // Also note that the preverifier doesn't like pop instructions + // that work on different operands. + if (branchTargetFinder.isBranchOrigin(newOffset1) || + branchTargetFinder.isBranchTarget(newOffset1) || + branchTargetFinder.isExceptionStart(newOffset1) || + branchTargetFinder.isExceptionEnd(newOffset1) || + branchTargetFinder.isInitializer(newOffset1) || + branchTargetFinder.isExceptionStart(newOffset2) || + branchTargetFinder.isExceptionEnd(newOffset2) || + isPop(code[newOffset1])) + { + break; + } + + // Make sure the new branch target was a branch target before, + // in order not to introduce new entries in the stack map table. + if (branchTargetFinder.isBranchTarget(newOffset2)) + { + successfulDelta = delta; + } + + if (branchTargetFinder.isBranchTarget(newOffset1)) + { + break; + } + } + } + + return successfulDelta; + } + + + /** + * Returns whether the given opcode represents a pop instruction that must + * get a consistent type (pop, pop2, arraylength). + */ + private boolean isPop(byte opcode) + { + return opcode == InstructionConstants.OP_POP || + opcode == InstructionConstants.OP_POP2 || + opcode == InstructionConstants.OP_ARRAYLENGTH; + } + + + /** + * Returns the whether there is a boundary of an exception block between + * the given offsets (including both). + */ + 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) + { + int offset = offset1; + offset1 = offset2; + offset2 = offset; + } + + // Check if there is a boundary of an exception block. + for (int offset = offset1; offset <= offset2; offset++) + { + if (branchTargetFinder.isExceptionStart(offset) || + branchTargetFinder.isExceptionEnd(offset)) + { + return true; + } + } + + return false; + } +} diff --git a/src/proguard/optimize/peephole/GotoGotoReplacer.java b/src/proguard/optimize/peephole/GotoGotoReplacer.java new file mode 100644 index 000000000..4a490a10b --- /dev/null +++ b/src/proguard/optimize/peephole/GotoGotoReplacer.java @@ -0,0 +1,115 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.peephole; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.editor.CodeAttributeEditor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor simplifies unconditional branches to other + * unconditional branches. + * + * @author Eric Lafortune + */ +public class GotoGotoReplacer +extends SimplifiedVisitor +implements InstructionVisitor +{ + private final CodeAttributeEditor codeAttributeEditor; + private final InstructionVisitor extraInstructionVisitor; + + + /** + * Creates a new GotoGotoReplacer. + * @param codeAttributeEditor a code editor that can be used for + * accumulating changes to the code. + */ + public GotoGotoReplacer(CodeAttributeEditor codeAttributeEditor) + { + this(codeAttributeEditor, null); + } + + + /** + * Creates a new GotoGotoReplacer. + * @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(CodeAttributeEditor codeAttributeEditor, + InstructionVisitor extraInstructionVisitor) + { + this.codeAttributeEditor = codeAttributeEditor; + this.extraInstructionVisitor = extraInstructionVisitor; + } + + + // 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) + { + // Check if the instruction is an unconditional goto instruction. + byte opcode = branchInstruction.opcode; + if (opcode == InstructionConstants.OP_GOTO || + opcode == InstructionConstants.OP_GOTO_W) + { + // Check if the goto instruction points to another simple goto + // instruction. + int branchOffset = branchInstruction.branchOffset; + int targetOffset = offset + branchOffset; + + if (branchOffset != 0 && + branchOffset != branchInstruction.length(offset) && + !codeAttributeEditor.isModified(offset) && + !codeAttributeEditor.isModified(targetOffset)) + { + Instruction targetInstruction = + InstructionFactory.create(codeAttribute.code, targetOffset); + + if (targetInstruction.opcode == InstructionConstants.OP_GOTO) + { + // Simplify the goto instruction. + int targetBranchOffset = ((BranchInstruction)targetInstruction).branchOffset; + + Instruction newBranchInstruction = + new BranchInstruction(opcode, + (branchOffset + targetBranchOffset)); + codeAttributeEditor.replaceInstruction(offset, + newBranchInstruction); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + extraInstructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + } + } + } + } +} diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/src/proguard/optimize/peephole/GotoReturnReplacer.java new file mode 100644 index 000000000..b6deec862 --- /dev/null +++ b/src/proguard/optimize/peephole/GotoReturnReplacer.java @@ -0,0 +1,115 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.peephole; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.editor.CodeAttributeEditor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor replaces unconditional branches to return instructions + * by these same return instructions. + * + * @author Eric Lafortune + */ +public class GotoReturnReplacer +extends SimplifiedVisitor +implements InstructionVisitor +{ + private final CodeAttributeEditor codeAttributeEditor; + private final InstructionVisitor extraInstructionVisitor; + + + /** + * Creates a new GotoReturnReplacer. + * @param codeAttributeEditor a code editor that can be used for + * accumulating changes to the code. + */ + public GotoReturnReplacer(CodeAttributeEditor codeAttributeEditor) + { + this(codeAttributeEditor, null); + } + + + /** + * Creates a new GotoReturnReplacer. + * @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(CodeAttributeEditor codeAttributeEditor, + InstructionVisitor extraInstructionVisitor) + { + this.codeAttributeEditor = codeAttributeEditor; + this.extraInstructionVisitor = extraInstructionVisitor; + } + + + // 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) + { + // Check if the instruction is an unconditional goto instruction. + byte opcode = branchInstruction.opcode; + if (opcode == InstructionConstants.OP_GOTO || + opcode == InstructionConstants.OP_GOTO_W) + { + // Check if the goto instruction points to a return instruction. + int targetOffset = offset + branchInstruction.branchOffset; + + if (!codeAttributeEditor.isModified(offset) && + !codeAttributeEditor.isModified(targetOffset)) + { + Instruction targetInstruction = InstructionFactory.create(codeAttribute.code, + targetOffset); + switch (targetInstruction.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: + // Replace the goto instruction by the return instruction. + Instruction returnInstruction = + new SimpleInstruction(targetInstruction.opcode); + codeAttributeEditor.replaceInstruction(offset, + returnInstruction); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + extraInstructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + + break; + } + } + } + } +} diff --git a/src/proguard/optimize/peephole/HorizontalClassMerger.java b/src/proguard/optimize/peephole/HorizontalClassMerger.java new file mode 100644 index 000000000..31d3d3357 --- /dev/null +++ b/src/proguard/optimize/peephole/HorizontalClassMerger.java @@ -0,0 +1,90 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor inlines siblings in the program classes that it visits, + * whenever possible. + * + * @see ClassMerger + * @author Eric Lafortune + */ +public class HorizontalClassMerger +extends SimplifiedVisitor +implements ClassVisitor +{ + private final boolean allowAccessModification; + private final boolean mergeInterfacesAggressively; + private final ClassVisitor extraClassVisitor; + + + /** + * Creates a new HorizontalClassMerger. + * @param allowAccessModification specifies whether the access modifiers + * of classes can be changed in order to + * merge them. + * @param mergeInterfacesAggressively specifies whether interfaces may + * be merged aggressively. + */ + public HorizontalClassMerger(boolean allowAccessModification, + boolean mergeInterfacesAggressively) + { + this(allowAccessModification, mergeInterfacesAggressively, null); + } + + + /** + * Creates a new VerticalClassMerger. + * @param allowAccessModification specifies whether the access modifiers + * of classes can be changed in order to + * merge them. + * @param mergeInterfacesAggressively specifies whether interfaces may + * be merged aggressively. + * @param extraClassVisitor an optional extra visitor for all + * merged classes. + */ + public HorizontalClassMerger(boolean allowAccessModification, + boolean mergeInterfacesAggressively, + ClassVisitor extraClassVisitor) + { + this.allowAccessModification = allowAccessModification; + this.mergeInterfacesAggressively = mergeInterfacesAggressively; + this.extraClassVisitor = extraClassVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.superClassConstantAccept(new ReferencedClassVisitor( + new SubclassTraveler( + new ProgramClassFilter( + new ClassMerger(programClass, + allowAccessModification, + mergeInterfacesAggressively, + extraClassVisitor))))); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/peephole/InstructionSequenceConstants.java b/src/proguard/optimize/peephole/InstructionSequenceConstants.java new file mode 100644 index 000000000..4ab9056c4 --- /dev/null +++ b/src/proguard/optimize/peephole/InstructionSequenceConstants.java @@ -0,0 +1,5090 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.*; +import proguard.classfile.instruction.*; +import proguard.classfile.visitor.ClassPrinter; + +/** + * This class contains a set of instruction sequences and their suggested + * replacements. + * + * @see InstructionSequencesReplacer + * @see InstructionSequenceReplacer + * @author Eric Lafortune + */ +public class InstructionSequenceConstants +{ + private static final int X = InstructionSequenceReplacer.X; + private static final int Y = InstructionSequenceReplacer.Y; + private static final int Z = InstructionSequenceReplacer.Z; + + private static final int A = InstructionSequenceReplacer.A; + private static final int B = InstructionSequenceReplacer.B; + private static final int C = InstructionSequenceReplacer.C; + private static final int D = InstructionSequenceReplacer.D; + + private static final int STRING_A_LENGTH = InstructionSequenceReplacer.STRING_A_LENGTH; + private static final int BOOLEAN_A_STRING = InstructionSequenceReplacer.BOOLEAN_A_STRING; + private static final int CHAR_A_STRING = InstructionSequenceReplacer.CHAR_A_STRING; + private static final int INT_A_STRING = InstructionSequenceReplacer.INT_A_STRING; + private static final int LONG_A_STRING = InstructionSequenceReplacer.LONG_A_STRING; + private static final int FLOAT_A_STRING = InstructionSequenceReplacer.FLOAT_A_STRING; + private static final int DOUBLE_A_STRING = InstructionSequenceReplacer.DOUBLE_A_STRING; + private static final int STRING_A_STRING = InstructionSequenceReplacer.STRING_A_STRING; + private static final int BOOLEAN_B_STRING = InstructionSequenceReplacer.BOOLEAN_B_STRING; + private static final int CHAR_B_STRING = InstructionSequenceReplacer.CHAR_B_STRING; + private static final int INT_B_STRING = InstructionSequenceReplacer.INT_B_STRING; + private static final int LONG_B_STRING = InstructionSequenceReplacer.LONG_B_STRING; + private static final int FLOAT_B_STRING = InstructionSequenceReplacer.FLOAT_B_STRING; + private static final int DOUBLE_B_STRING = InstructionSequenceReplacer.DOUBLE_B_STRING; + private static final int STRING_B_STRING = InstructionSequenceReplacer.STRING_B_STRING; + + 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 STRING_EMPTY = 31; + + private static final int FIELD_I = 32; // Implicitly uses X and Y. + private static final int FIELD_L = 33; // Implicitly uses X and Y. + private static final int FIELD_F = 34; // Implicitly uses X and Y. + private static final int FIELD_D = 35; // Implicitly uses X and Y. + + private static final int METHOD_STRING_EQUALS = 36; + private static final int METHOD_STRING_LENGTH = 37; + private static final int METHOD_STRING_VALUEOF_Z = 38; + private static final int METHOD_STRING_VALUEOF_C = 39; + private static final int METHOD_STRING_VALUEOF_I = 40; + private static final int METHOD_STRING_VALUEOF_J = 41; + private static final int METHOD_STRING_VALUEOF_F = 42; + private static final int METHOD_STRING_VALUEOF_D = 43; + private static final int METHOD_STRING_VALUEOF_OBJECT = 44; + private static final int METHOD_STRINGBUFFER_INIT = 45; + private static final int METHOD_STRINGBUFFER_INIT_STRING = 46; + private static final int METHOD_STRINGBUFFER_APPEND_Z = 47; + private static final int METHOD_STRINGBUFFER_APPEND_C = 48; + private static final int METHOD_STRINGBUFFER_APPEND_I = 49; + private static final int METHOD_STRINGBUFFER_APPEND_J = 50; + private static final int METHOD_STRINGBUFFER_APPEND_F = 51; + private static final int METHOD_STRINGBUFFER_APPEND_D = 52; + private static final int METHOD_STRINGBUFFER_APPEND_STRING = 53; + private static final int METHOD_STRINGBUFFER_APPEND_OBJECT = 54; + private static final int METHOD_STRINGBUFFER_LENGTH = 55; + private static final int METHOD_STRINGBUFFER_TOSTRING = 56; + private static final int METHOD_STRINGBUILDER_INIT = 57; + private static final int METHOD_STRINGBUILDER_INIT_STRING = 58; + private static final int METHOD_STRINGBUILDER_APPEND_Z = 59; + private static final int METHOD_STRINGBUILDER_APPEND_C = 60; + private static final int METHOD_STRINGBUILDER_APPEND_I = 61; + private static final int METHOD_STRINGBUILDER_APPEND_J = 62; + private static final int METHOD_STRINGBUILDER_APPEND_F = 63; + private static final int METHOD_STRINGBUILDER_APPEND_D = 64; + private static final int METHOD_STRINGBUILDER_APPEND_STRING = 65; + private static final int METHOD_STRINGBUILDER_APPEND_OBJECT = 66; + private static final int METHOD_STRINGBUILDER_LENGTH = 67; + private static final int METHOD_STRINGBUILDER_TOSTRING = 68; + + private static final int CLASS_STRING = 69; + private static final int CLASS_STRINGBUFFER = 70; + private static final int CLASS_STRINGBUILDER = 71; + + private static final int NAME_AND_TYPE_I = 72; // Implicitly uses Y. + private static final int NAME_AND_TYPE_L = 73; // Implicitly uses Y. + private static final int NAME_AND_TYPE_F = 74; // Implicitly uses Y. + private static final int NAME_AND_TYPE_D = 75; // Implicitly uses Y. + + private static final int NAME_AND_TYPE_EQUALS = 76; + private static final int NAME_AND_TYPE_LENGTH = 77; + private static final int NAME_AND_TYPE_VALUEOF_Z = 78; + private static final int NAME_AND_TYPE_VALUEOF_C = 79; + private static final int NAME_AND_TYPE_VALUEOF_I = 80; + private static final int NAME_AND_TYPE_VALUEOF_J = 81; + private static final int NAME_AND_TYPE_VALUEOF_F = 82; + private static final int NAME_AND_TYPE_VALUEOF_D = 83; + private static final int NAME_AND_TYPE_VALUEOF_OBJECT = 84; + private static final int NAME_AND_TYPE_INIT = 85; + private static final int NAME_AND_TYPE_INIT_STRING = 86; + private static final int NAME_AND_TYPE_APPEND_Z_STRINGBUFFER = 87; + private static final int NAME_AND_TYPE_APPEND_C_STRINGBUFFER = 88; + private static final int NAME_AND_TYPE_APPEND_I_STRINGBUFFER = 89; + private static final int NAME_AND_TYPE_APPEND_J_STRINGBUFFER = 90; + private static final int NAME_AND_TYPE_APPEND_F_STRINGBUFFER = 91; + private static final int NAME_AND_TYPE_APPEND_D_STRINGBUFFER = 92; + private static final int NAME_AND_TYPE_APPEND_STRING_STRINGBUFFER = 93; + private static final int NAME_AND_TYPE_APPEND_OBJECT_STRINGBUFFER = 94; + private static final int NAME_AND_TYPE_APPEND_Z_STRINGBUILDER = 95; + private static final int NAME_AND_TYPE_APPEND_C_STRINGBUILDER = 96; + private static final int NAME_AND_TYPE_APPEND_I_STRINGBUILDER = 97; + private static final int NAME_AND_TYPE_APPEND_J_STRINGBUILDER = 98; + private static final int NAME_AND_TYPE_APPEND_F_STRINGBUILDER = 99; + private static final int NAME_AND_TYPE_APPEND_D_STRINGBUILDER = 100; + private static final int NAME_AND_TYPE_APPEND_STRING_STRINGBUILDER = 101; + private static final int NAME_AND_TYPE_APPEND_OBJECT_STRINGBUILDER = 102; + private static final int NAME_AND_TYPE_TOSTRING = 103; + + private static final int UTF8_EMPTY = 104; + private static final int UTF8_I = 105; + private static final int UTF8_L = 106; + private static final int UTF8_F = 107; + private static final int UTF8_D = 108; + private static final int UTF8_STRING = 109; + private static final int UTF8_STRINGBUFFER = 110; + private static final int UTF8_STRINGBUILDER = 111; + private static final int UTF8_EQUALS = 112; + private static final int UTF8_OBJECT_Z = 113; + private static final int UTF8_LENGTH = 114; + private static final int UTF8__I = 115; + private static final int UTF8_VALUEOF = 116; + private static final int UTF8_Z_STRING = 117; + private static final int UTF8_C_STRING = 118; + private static final int UTF8_I_STRING = 119; + private static final int UTF8_J_STRING = 120; + private static final int UTF8_F_STRING = 121; + private static final int UTF8_D_STRING = 122; + private static final int UTF8_OBJECT_STRING = 123; + private static final int UTF8_INIT = 124; + private static final int UTF8__VOID = 125; + private static final int UTF8_STRING_VOID = 126; + private static final int UTF8_TOSTRING = 127; + private static final int UTF8__STRING = 128; + private static final int UTF8_APPEND = 129; + private static final int UTF8_Z_STRINGBUFFER = 130; + private static final int UTF8_C_STRINGBUFFER = 131; + private static final int UTF8_I_STRINGBUFFER = 132; + private static final int UTF8_J_STRINGBUFFER = 133; + private static final int UTF8_F_STRINGBUFFER = 134; + private static final int UTF8_D_STRINGBUFFER = 135; + private static final int UTF8_STRING_STRINGBUFFER = 136; + private static final int UTF8_OBJECT_STRINGBUFFER = 137; + private static final int UTF8_Z_STRINGBUILDER = 138; + private static final int UTF8_C_STRINGBUILDER = 139; + private static final int UTF8_I_STRINGBUILDER = 140; + private static final int UTF8_J_STRINGBUILDER = 141; + private static final int UTF8_F_STRINGBUILDER = 142; + private static final int UTF8_D_STRINGBUILDER = 143; + private static final int UTF8_STRING_STRINGBUILDER = 144; + private static final int UTF8_OBJECT_STRINGBUILDER = 145; + + private static final int SENTINEL = 146; + + + public static final Constant[] 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 StringConstant(UTF8_EMPTY, null, null), + + 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 MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_EQUALS, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_LENGTH, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_Z, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_C, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_I, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_J, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_F, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_D, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_OBJECT, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_INIT, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_INIT_STRING, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_Z_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_C_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_I_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_J_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_F_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_D_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_STRING_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_OBJECT_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_LENGTH, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_TOSTRING, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_INIT, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_INIT_STRING, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_Z_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_C_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_I_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_J_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_F_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_D_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_STRING_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_OBJECT_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_LENGTH, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_TOSTRING, null, null), + + new ClassConstant(UTF8_STRING, null), + new ClassConstant(UTF8_STRINGBUFFER, null), + new ClassConstant(UTF8_STRINGBUILDER, null), + + new NameAndTypeConstant(Y, UTF8_I), + new NameAndTypeConstant(Y, UTF8_L), + new NameAndTypeConstant(Y, UTF8_F), + new NameAndTypeConstant(Y, UTF8_D), + new NameAndTypeConstant(UTF8_EQUALS, UTF8_OBJECT_Z), + new NameAndTypeConstant(UTF8_LENGTH, UTF8__I), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_Z_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_C_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_I_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_J_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_F_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_D_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_OBJECT_STRING), + new NameAndTypeConstant(UTF8_INIT, UTF8__VOID), + new NameAndTypeConstant(UTF8_INIT, UTF8_STRING_VOID), + new NameAndTypeConstant(UTF8_APPEND, UTF8_Z_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_C_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_I_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_J_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_F_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_D_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_STRING_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_OBJECT_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_Z_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_C_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_I_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_J_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_F_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_D_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_STRING_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_OBJECT_STRINGBUILDER), + new NameAndTypeConstant(UTF8_TOSTRING, UTF8__STRING), + + new Utf8Constant(""), + new Utf8Constant("I"), + new Utf8Constant("J"), + new Utf8Constant("F"), + new Utf8Constant("D"), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING_BUILDER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_EQUALS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_EQUALS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_LENGTH), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_LENGTH), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_VALUEOF), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_BOOLEAN), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_CHAR), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_INT), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_LONG), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_FLOAT), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_DOUBLE), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_OBJECT), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_INIT), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_INIT), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_STRING_VOID), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_TOSTRING), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_TOSTRING), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_APPEND), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_BOOLEAN_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CHAR_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_INT_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_LONG_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_FLOAT_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOUBLE_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_STRING_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_OBJECT_STRING_BUFFER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_BOOLEAN_STRING_BUILDER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CHAR_STRING_BUILDER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_INT_STRING_BUILDER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_LONG_STRING_BUILDER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_FLOAT_STRING_BUILDER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOUBLE_STRING_BUILDER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_STRING_STRING_BUILDER), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_OBJECT_STRING_BUILDER), + }; + + public static final Instruction[][][] VARIABLE = new Instruction[][][] + { + { // nop = nothing + { + new SimpleInstruction(InstructionConstants.OP_NOP), + },{ + // Nothing. + }, + }, + { // iload/pop = nothing + { + new VariableInstruction(InstructionConstants.OP_ILOAD, X), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // lload/pop2 = nothing + { + new VariableInstruction(InstructionConstants.OP_LLOAD, X), + new SimpleInstruction(InstructionConstants.OP_POP2), + },{ + // Nothing. + }, + }, + { // fload/pop = nothing + { + new VariableInstruction(InstructionConstants.OP_FLOAD, X), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // dload/pop2 = nothing + { + new VariableInstruction(InstructionConstants.OP_DLOAD, X), + new SimpleInstruction(InstructionConstants.OP_POP2), + },{ + // Nothing. + }, + }, + { // aload/pop = nothing + { + new VariableInstruction(InstructionConstants.OP_ALOAD, X), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // i = i = nothing + { + new VariableInstruction(InstructionConstants.OP_ILOAD, X), + 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), + }, + }, + }; + + public static final Instruction[][][] ARITHMETIC = new Instruction[][][] + { + { // 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_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), + }, + }, + { // 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_SIPUSH, 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. + }, + }, + // Not valid for -0.0. +// { // ... + 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), + }, + }, + // Not valid for -0.0 and for NaN. +// { // ... * 0f = 0f +// { +// 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), + }, + }, + // Not valid for -0.0 and for NaN. +// { // ... * 0d = 0d +// { +// 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. + }, + }, + // Not valid for negative values. +// { // ... / 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. + }, + }, + // Not valid for negative values. +// { // ... / 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), + }, + }, + // Not valid for negative values. +// { // ... % 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. + }, + }, + }; + + public static final Instruction[][][] FIELD = new Instruction[][][] + { + { // 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), + }, + }, + }; + + public static final Instruction[][][] CAST = new Instruction[][][] + { + { // (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. + }, + }, + { // (X)(X)... = (X)... + { + new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X), + new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X), + },{ + new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X), + }, + }, + // 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), +// }, +// }, + }; + + public static final Instruction[][][] BRANCH = new Instruction[][][] + { + { // 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 == i) = iload/ifeq + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPEQ, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFEQ, X), + }, + }, + { // if (0 == i) = getstatic/ifeq + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPEQ, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFEQ, X), + }, + }, + { // if (0 == i) = getfield/ifeq + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPEQ, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + 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 != i) = iload/ifeq + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPNE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFNE, X), + }, + }, + { // if (0 != i) = getstatic/ifeq + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPNE, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFNE, X), + }, + }, + { // if (0 != i) = getfield/ifeq + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPNE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + 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 (... < 1) = ifle + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new BranchInstruction(InstructionConstants.OP_IFICMPLT, X), + },{ + new BranchInstruction(InstructionConstants.OP_IFLE, X), + }, + }, + { // if (0 > i) = iload/iflt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPGT, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFLT, X), + }, + }, + { // if (1 > i) = iload/ifle + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPGT, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFLE, X), + }, + }, + { // if (0 > i) = getstatic/iflt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPGT, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFLT, X), + }, + }, + { // if (1 > i) = getstatic/ifle + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPGT, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFLE, X), + }, + }, + { // if (0 > i) = getfield/iflt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPGT, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFLT, X), + }, + }, + { // if (1 > i) = getfield/ifle + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPGT, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFLE, X), + }, + }, + { // if (... >= 0) = ifge + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new BranchInstruction(InstructionConstants.OP_IFICMPGE, X), + },{ + new BranchInstruction(InstructionConstants.OP_IFGE, X), + }, + }, + { // if (... >= 1) = ifgt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new BranchInstruction(InstructionConstants.OP_IFICMPGE, X), + },{ + new BranchInstruction(InstructionConstants.OP_IFGT, X), + }, + }, + { // if (0 <= i) = iload/ifge + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPLE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFGE, X), + }, + }, + { // if (1 <= i) = iload/ifgt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPLE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFGT, X), + }, + }, + { // if (0 <= i) = getstatic/ifge + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPLE, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFGE, X), + }, + }, + { // if (1 <= i) = getstatic/ifgt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPLE, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFGT, X), + }, + }, + { // if (0 <= i) = getfield/ifge + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPLE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFGE, X), + }, + }, + { // if (1 <= i) = getfield/ifgt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPLE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFGT, X), + }, + }, + { // if (... > 0) = ifgt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new BranchInstruction(InstructionConstants.OP_IFICMPGT, X), + },{ + new BranchInstruction(InstructionConstants.OP_IFGT, X), + }, + }, + { // if (... > -1) = ifge + { + new SimpleInstruction(InstructionConstants.OP_ICONST_M1), + new BranchInstruction(InstructionConstants.OP_IFICMPGT, X), + },{ + new BranchInstruction(InstructionConstants.OP_IFGE, X), + }, + }, + { // if (0 < i) = iload/ifgt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPLT, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFGT, X), + }, + }, + { // if (-1 < i) = iload/ifge + { + new SimpleInstruction(InstructionConstants.OP_ICONST_M1), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPLT, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFGE, X), + }, + }, + { // if (0 < i) = getstatic/ifgt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPLT, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFGT, X), + }, + }, + { // if (-1 < i) = getstatic/ifge + { + new SimpleInstruction(InstructionConstants.OP_ICONST_M1), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPLT, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFGE, X), + }, + }, + { // if (0 < i) = getfield/ifgt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPLT, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFGT, X), + }, + }, + { // if (-1 < i) = getfield/ifge + { + new SimpleInstruction(InstructionConstants.OP_ICONST_M1), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPLT, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFGE, X), + }, + }, + { // if (... <= 0) = ifle + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new BranchInstruction(InstructionConstants.OP_IFICMPLE, X), + },{ + new BranchInstruction(InstructionConstants.OP_IFLE, X), + }, + }, + { // if (... <= -1) = iflt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_M1), + new BranchInstruction(InstructionConstants.OP_IFICMPLE, X), + },{ + new BranchInstruction(InstructionConstants.OP_IFLT, X), + }, + }, + { // if (0 >= i) = iload/ifle + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPGE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFLE, X), + }, + }, + { // if (-1 >= i) = iload/iflt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_M1), + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPGE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFLT, X), + }, + }, + { // if (0 >= i) = getstatic/ifle + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPGE, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFLE, X), + }, + }, + { // if (-1 >= i) = getstatic/iflt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_M1), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFICMPGE, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFLT, X), + }, + }, + { // if (0 >= i) = getfield/ifle + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPGE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFLE, X), + }, + }, + { // if (-1 >= i) = getfield/iflt + { + new SimpleInstruction(InstructionConstants.OP_ICONST_M1), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFICMPGE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFLT, X), + }, + }, + { // if (... == null) = ifnull + { + new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), + new BranchInstruction(InstructionConstants.OP_IFACMPEQ, X), + },{ + new BranchInstruction(InstructionConstants.OP_IFNULL, X), + }, + }, + { // if (null == a) = aload/ifnull + { + new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFACMPEQ, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFNULL, X), + }, + }, + { // if (null == a) = getstatic/ifnull + { + new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFACMPEQ, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFNULL, X), + }, + }, + { // if (null == a) = getfield/ifnull + { + new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFACMPEQ, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + 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), + }, + }, + { // if (null != a) = aload/ifnonnull + { + new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFACMPNE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new BranchInstruction(InstructionConstants.OP_IFNONNULL, X), + }, + }, + { // if (null != a) = getstatic/ifnonnull + { + new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFACMPNE, X), + },{ + new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y), + new BranchInstruction(InstructionConstants.OP_IFNONNULL, X), + }, + }, + { // if (null != a) = getfield/ifnonnull + { + new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + new BranchInstruction(InstructionConstants.OP_IFACMPNE, X), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, Y), + new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z), + 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), +// }, +// } + }; + + public static final Instruction[][][] STRING = new Instruction[][][] + { + { // "...".equals("...") = true + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRING_EQUALS), + },{ + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + }, + }, + { // "...".length() = ... + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRING_LENGTH), + },{ + new SimpleInstruction(InstructionConstants.OP_SIPUSH, STRING_A_LENGTH), + }, + }, + { // String.valueOf(Z) = ".... + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, BOOLEAN_A_STRING), + }, + }, + { // String.valueOf(C) = "...." + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + }, + }, + { // String.valueOf(Cc) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + }, + }, + { // String.valueOf(I) = "...." + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + }, + }, + { // String.valueOf(Ic) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + }, + }, + { // String.valueOf(J) = "...." + { + new SimpleInstruction(InstructionConstants.OP_LCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + }, + }, + { // String.valueOf(Jc) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + }, + }, + { // String.valueOf(F) = "...." + { + new SimpleInstruction(InstructionConstants.OP_FCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + }, + }, + { // String.valueOf(Fc) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + }, + }, + { // String.valueOf(D) = "...." + { + new SimpleInstruction(InstructionConstants.OP_DCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + }, + }, + { // String.valueOf(Dc) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + }, + }, + + { // new StringBuffer("...").toString() = "..." (ignoring identity) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, A), + }, + }, + { // new StringBuffer(string).toString() = string (ignoring identity) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + }, + }, + { // new StringBuffer("...").length() = length + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_LENGTH), + },{ + new SimpleInstruction(InstructionConstants.OP_SIPUSH, STRING_A_LENGTH), + }, + }, + { // new StringBuffer() (without dup) = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...") (without dup) = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + },{ + // Nothing. + }, + }, + { // new StringBuffer()/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...")/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(z)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(c)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(i)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(l)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_LLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(f)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_FLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(d)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_DLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(s)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ALOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // StringBuffer#toString()/pop = pop + { + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + new SimpleInstruction(InstructionConstants.OP_POP), + }, + }, + { // StringBuffer#append("") = nothing + { + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_EMPTY), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + },{ + // Nothing. + }, + }, + { // new StringBuffer().append(Z) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, BOOLEAN_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(C) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Cc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(I) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Ic) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(J) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Jc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(F) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Fc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(D) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Dc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append("...") = new StringBuffer("...") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Z) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | BOOLEAN_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(C) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Cc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(I) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Ic) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(J) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Jc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(F) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Fc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(D) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Dc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append("...") = new StringBuffer("......") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | STRING_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // StringBuffer#append("...").append(Z) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | BOOLEAN_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(C) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Cc) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(I) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Ic) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(J) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Jc) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(F) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Fc) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(D) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Dc) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append("...") = StringBuffer#append("......") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | STRING_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // new StringBuffer().append(z).toString() = String.valueOf(z) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_Z), + }, + }, + { // new StringBuffer().append(c).toString() = String.valueOf(c) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_C), + }, + }, + { // new StringBuffer().append(i).toString() = String.valueOf(i) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_I), + }, + }, + { // new StringBuffer().append(j).toString() = String.valueOf(j) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_LLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_LLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_J), + }, + }, + { // new StringBuffer().append(f).toString() = String.valueOf(f) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_FLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_FLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_F), + }, + }, + { // new StringBuffer().append(d).toString() = String.valueOf(d) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_DLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_DLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_D), + }, + }, + { // new StringBuffer().append(string).toString() = string + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + }, + }, + { // new StringBuffer().append(object).toString() = String.valueOf(object) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_OBJECT), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_OBJECT), + }, + }, + + { // new StringBuilder("...").toString() = "..." (ignoring identity) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, A), + }, + }, + { // new StringBuilder(string).toString() = string (ignoring identity) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + }, + }, + { // new StringBuilder("...").length() = length + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_LENGTH), + },{ + new SimpleInstruction(InstructionConstants.OP_SIPUSH, STRING_A_LENGTH), + }, + }, + { // new StringBuilder() (without dup) = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...") (without dup) = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + },{ + // Nothing. + }, + }, + { // new StringBuilder()/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...")/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(z)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(c)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(i)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(l)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_LLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(f)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_FLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(d)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_DLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(s)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ALOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // StringBuilder#toString()/pop = pop + { + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + new SimpleInstruction(InstructionConstants.OP_POP), + }, + }, + { // StringBuilder#append("") = nothing + { + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_EMPTY), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + },{ + // Nothing. + }, + }, + { // new StringBuilder().append(Z) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, BOOLEAN_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(C) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Cc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(I) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Ic) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(J) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Jc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(F) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Fc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(D) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Dc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append("...") = new StringBuilder("...") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Z) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | BOOLEAN_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(C) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Cc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(I) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Ic) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(J) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Jc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(F) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Fc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(D) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Dc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append("...") = new StringBuilder("......") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | STRING_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // StringBuilder#append("...").append(Z) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | BOOLEAN_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(C) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Cc) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(I) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Ic) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(J) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Jc) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(F) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Fc) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(D) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Dc) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append("...") = StringBuilder#append("......") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | STRING_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // new StringBuilder().append(z).toString() = String.valueOf(z) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_Z), + }, + }, + { // new StringBuilder().append(c).toString() = String.valueOf(c) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_C), + }, + }, + { // new StringBuilder().append(i).toString() = String.valueOf(i) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_I), + }, + }, + { // new StringBuilder().append(j).toString() = String.valueOf(j) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_LLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_LLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_J), + }, + }, + { // new StringBuilder().append(f).toString() = String.valueOf(f) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_FLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_FLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_F), + }, + }, + { // new StringBuilder().append(d).toString() = String.valueOf(d) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_DLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_DLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_D), + }, + }, + { // new StringBuilder().append(string).toString() = string + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + }, + }, + { // new StringBuilder().append(object).toString() = String.valueOf(object) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_OBJECT), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_OBJECT), + }, + }, + }; + + + /** + * Prints out the constants and the instruction sequences. + */ + public static void main(String[] args) + { + ProgramClass clazz = new ProgramClass(); + clazz.constantPool = CONSTANTS; + + ClassPrinter printer = new ClassPrinter(); + + for (int index = 0; index < CONSTANTS.length; index++) + { + System.out.print("["+index+"] "); + try + { + CONSTANTS[index].accept(clazz, printer); + } + catch (Exception e) + { + System.out.println("("+e.getClass().getName()+")"); + } + } + + if (CONSTANTS.length != SENTINEL) + { + throw new IllegalStateException("Constants length ["+CONSTANTS.length+"] different from number of constant names ["+SENTINEL+"]"); + } + + Instruction[][][] sequences = STRING; + + for (int sequence = 0; sequence < sequences.length; sequence++) + { + System.out.println(); + Instruction[] instructions = sequences[sequence][0]; + for (int index = 0; index < instructions.length; index++) + { + Instruction instruction = instructions[index]; + try + { + instruction.accept(clazz, null, null, index, new ClassPrinter()); + } + catch (Exception e) {} + } + + System.out.println("=>"); + instructions = sequences[sequence][1]; + for (int index = 0; index < instructions.length; index++) + { + Instruction instruction = instructions[index]; + try + { + instruction.accept(clazz, null, null, index, new ClassPrinter()); + } + catch (Exception e) {} + } + } + } +} diff --git a/src/proguard/optimize/peephole/InstructionSequenceReplacer.java b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java new file mode 100644 index 000000000..7ec1a95af --- /dev/null +++ b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java @@ -0,0 +1,420 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.*; +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; + /*/ + public static boolean DEBUG = true; + //*/ + + 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 BOOLEAN_STRING = 0x1; + private static final int CHAR_STRING = 0x2; + private static final int INT_STRING = 0x3; + private static final int LONG_STRING = 0x4; + private static final int FLOAT_STRING = 0x5; + private static final int DOUBLE_STRING = 0x6; + private static final int STRING_STRING = 0x7; + + public static final int STRING_A_LENGTH = 0x20000000; + public static final int BOOLEAN_A_STRING = 0x20000001; + public static final int CHAR_A_STRING = 0x20000002; + public static final int INT_A_STRING = 0x20000003; + public static final int LONG_A_STRING = 0x20000004; + public static final int FLOAT_A_STRING = 0x20000005; + public static final int DOUBLE_A_STRING = 0x20000006; + public static final int STRING_A_STRING = 0x20000007; + public static final int BOOLEAN_B_STRING = 0x20000010; + public static final int CHAR_B_STRING = 0x20000020; + public static final int INT_B_STRING = 0x20000030; + public static final int LONG_B_STRING = 0x20000040; + public static final int FLOAT_B_STRING = 0x20000050; + public static final int DOUBLE_B_STRING = 0x20000060; + public static final int STRING_B_STRING = 0x20000070; + + + private final InstructionSequenceMatcher instructionSequenceMatcher; + private final Constant[] patternConstants; + 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.patternConstants = patternConstants; + 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(clazz, index).shrink().toString(matchedOffset)); + } + } + + // Replace the instruction sequence. + for (int index = 0; index < replacementInstructions.length; index++) + { + codeAttributeEditor.replaceInstruction(instructionSequenceMatcher.matchedInstructionOffset(index), + replacementInstructionFactory.create(clazz, index)); + } + + // 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(Clazz clazz, int index) + { + // Create the instruction. + replacementInstructions[index].accept(clazz, + null, + null, + instructionSequenceMatcher.matchedInstructionOffset(index), + this); + + // Return it. + return replacementInstruction; + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + replacementInstruction = + new SimpleInstruction(simpleInstruction.opcode, + matchedArgument(clazz, 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, + matchedConstantIndex((ProgramClass)clazz, + 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)); + } + + + /** + * Returns the matched argument for the given pattern argument. + */ + private int matchedArgument(Clazz clazz, int argument) + { + // Special case: do we have to compute the string length? + if (argument == STRING_A_LENGTH) + { + // Return the string length. + return clazz.getStringString(instructionSequenceMatcher.matchedArgument(A)).length(); + } + + // Otherwise, just return the matched argument. + return instructionSequenceMatcher.matchedArgument(argument); + } + + + /** + * Returns the matched or newly created constant index for the given + * pattern constant index. + */ + private int matchedConstantIndex(ProgramClass programClass, int constantIndex) + { + // Special case: do we have to create a concatenated string? + if (constantIndex >= BOOLEAN_A_STRING && + constantIndex <= (STRING_A_STRING | STRING_B_STRING)) + { + // Create a new string constant and return its index. + return new ConstantPoolEditor(programClass).addStringConstant( + argumentAsString(programClass, constantIndex & 0xf, A) + + argumentAsString(programClass, (constantIndex >>> 4) & 0xf, B), + null, + null); + } + + int matchedConstantIndex = + instructionSequenceMatcher.matchedConstantIndex(constantIndex); + + // Do we have a matched constant index? + if (matchedConstantIndex > 0) + { + // Return its index. + return matchedConstantIndex; + } + + // Otherwise, we still have to create a new constant. + // This currently only works for constants without any wildcards. + ProgramClass dummyClass = new ProgramClass(); + dummyClass.constantPool = patternConstants; + + return new ConstantAdder(programClass).addConstant(dummyClass, constantIndex); + } + + + private String argumentAsString(ProgramClass programClass, + int valueType, + int argument) + { + switch (valueType) + { + case BOOLEAN_STRING: + return Boolean.toString((instructionSequenceMatcher.wasConstant(argument) ? + ((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)) != 0); + + case CHAR_STRING: + return Character.toString((char)(instructionSequenceMatcher.wasConstant(argument) ? + ((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument))); + + case INT_STRING: + return Integer.toString(instructionSequenceMatcher.wasConstant(argument) ? + ((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)); + + case LONG_STRING: + return Long.toString(instructionSequenceMatcher.wasConstant(argument) ? + ((LongConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)); + + case FLOAT_STRING: + return Float.toString(instructionSequenceMatcher.wasConstant(argument) ? + ((FloatConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)); + + case DOUBLE_STRING: + return Double.toString(instructionSequenceMatcher.wasConstant(argument) ? + ((DoubleConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)); + + case STRING_STRING: + return + programClass.getStringString(instructionSequenceMatcher.matchedConstantIndex(argument)); + + default: + return ""; + } + } + } +} diff --git a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java b/src/proguard/optimize/peephole/InstructionSequencesReplacer.java new file mode 100644 index 000000000..22fb6cd46 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/MemberPrivatizer.java b/src/proguard/optimize/peephole/MemberPrivatizer.java new file mode 100644 index 000000000..f57281c36 --- /dev/null +++ b/src/proguard/optimize/peephole/MemberPrivatizer.java @@ -0,0 +1,103 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 + * privatized methods still have to be fixed. + * + * @see NonPrivateMemberMarker + * @see MethodInvocationFixer + * @author Eric Lafortune + */ +public class MemberPrivatizer +extends SimplifiedVisitor +implements MemberVisitor +{ + private final MemberVisitor extraMemberVisitor; + + + /** + * Creates a new MemberPrivatizer. + */ + public MemberPrivatizer() + { + this(null); + } + + + /** + * Creates a new MemberPrivatizer. + * @param extraMemberVisitor an optional extra visitor for all privatized + * class members. + */ + public MemberPrivatizer(MemberVisitor extraMemberVisitor) + { + this.extraMemberVisitor = extraMemberVisitor; + } + + + // 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 (extraMemberVisitor != null) + { + extraMemberVisitor.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 (extraMemberVisitor != null) + { + extraMemberVisitor.visitProgramMethod(programClass, programMethod); + } + } + } +} diff --git a/src/proguard/optimize/peephole/MethodFinalizer.java b/src/proguard/optimize/peephole/MethodFinalizer.java new file mode 100644 index 000000000..89174ac49 --- /dev/null +++ b/src/proguard/optimize/peephole/MethodFinalizer.java @@ -0,0 +1,93 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 MemberVisitor makes the program methods that it visits + * final, if possible. + * + * @author Eric Lafortune + */ +public class MethodFinalizer +extends SimplifiedVisitor +implements MemberVisitor +{ + private final MemberVisitor extraMemberVisitor; + + private final MemberFinder memberFinder = new MemberFinder(); + + + /** + * Creates a new ClassFinalizer. + */ + public MethodFinalizer() + { + this(null); + } + + + /** + * Creates a new ClassFinalizer. + * @param extraMemberVisitor an optional extra visitor for all finalized + * methods. + */ + public MethodFinalizer(MemberVisitor extraMemberVisitor) + { + this.extraMemberVisitor = extraMemberVisitor; + } + + + // 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); + } + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/peephole/MethodInliner.java b/src/proguard/optimize/peephole/MethodInliner.java new file mode 100644 index 000000000..947cd43d6 --- /dev/null +++ b/src/proguard/optimize/peephole/MethodInliner.java @@ -0,0 +1,599 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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.*; +import proguard.optimize.*; +import proguard.optimize.info.*; + +import java.util.*; + +/** + * This AttributeVisitor inlines short methods or methods that are only invoked + * once, in the code attributes that it visits. + * + * @author Eric Lafortune + */ +public class MethodInliner +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + MemberVisitor +{ + private static final int MAXIMUM_INLINED_CODE_LENGTH = Integer.parseInt(System.getProperty("maximum.inlined.code.length", "8")); + private static final int MAXIMUM_RESULTING_CODE_LENGTH_JSE = Integer.parseInt(System.getProperty("maximum.resulting.code.length", "7000")); + private static final int MAXIMUM_RESULTING_CODE_LENGTH_JME = Integer.parseInt(System.getProperty("maximum.resulting.code.length", "2000")); + + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = true; + //*/ + + + private final boolean microEdition; + 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 StackSizeComputer stackSizeComputer = new StackSizeComputer(); + + private ProgramClass targetClass; + private ProgramMethod targetMethod; + private ConstantAdder constantAdder; + private ExceptionInfoAdder exceptionInfoAdder; + private int estimatedResultingCodeLength; + private boolean inlining; + private Stack inliningMethods = new Stack(); + private boolean emptyInvokingStack; + private int uninitializedObjectCount; + private int variableOffset; + private boolean inlined; + private boolean inlinedAny; + + + /** + * Creates a new MethodInliner. + * @param microEdition indicates whether the resulting code is + * targeted at Java Micro Edition. + * @param allowAccessModification indicates whether the access modifiers of + * classes and class members can be changed + * in order to inline methods. + * @param inlineSingleInvocations indicates whether the single invocations + * should be inlined, or, alternatively, + * short methods. + */ + public MethodInliner(boolean microEdition, + boolean allowAccessModification, + boolean inlineSingleInvocations) + { + this(microEdition, + allowAccessModification, + inlineSingleInvocations, + null); + } + + + /** + * Creates a new MethodInliner. + * @param microEdition indicates whether the resulting code is + * targeted at Java Micro Edition. + * @param allowAccessModification indicates whether the access modifiers of + * classes and class members can be changed + * in order to inline methods. + * @param inlineSingleInvocations indicates whether the single invocations + * should be inlined, or, alternatively, + * short methods. + * @param extraInlinedInvocationVisitor an optional extra visitor for all + * inlined invocation instructions. + */ + public MethodInliner(boolean microEdition, + boolean allowAccessModification, + boolean inlineSingleInvocations, + InstructionVisitor extraInlinedInvocationVisitor) + { + this.microEdition = microEdition; + 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) + { + // TODO: Remove this when the method inliner 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 inlining method:"); + System.err.println(" Target class = ["+targetClass.getName()+"]"); + System.err.println(" Target method = ["+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); + if (inlining) + { + System.err.println(" Inlined class = ["+clazz.getName()+"]"); + System.err.println(" Inlined method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + } + System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + System.err.println("Not inlining this method"); + + if (DEBUG) + { + targetMethod.accept(targetClass, new ClassPrinter()); + if (inlining) + { + method.accept(clazz, new ClassPrinter()); + } + + throw ex; + } + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (!inlining) + { +// codeAttributeComposer.DEBUG = DEBUG = +// clazz.getName().equals("abc/Def") && +// method.getName(clazz).equals("abc"); + + targetClass = (ProgramClass)clazz; + targetMethod = (ProgramMethod)method; + constantAdder = new ConstantAdder(targetClass); + exceptionInfoAdder = new ExceptionInfoAdder(targetClass, codeAttributeComposer); + estimatedResultingCodeLength = codeAttribute.u4codeLength; + inliningMethods.clear(); + uninitializedObjectCount = method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 1 : 0; + inlinedAny = false; + codeAttributeComposer.reset(); + stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute); + + // Append the body of the code. + copyCode(clazz, method, codeAttribute); + + targetClass = null; + targetMethod = null; + constantAdder = 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. + else if ((inlineSingleInvocations ? + MethodInvocationMarker.getInvocationCount(method) == 1 : + codeAttribute.u4codeLength <= MAXIMUM_INLINED_CODE_LENGTH) && + estimatedResultingCodeLength + codeAttribute.u4codeLength < + (microEdition ? + MAXIMUM_RESULTING_CODE_LENGTH_JME : + MAXIMUM_RESULTING_CODE_LENGTH_JSE)) + { + if (DEBUG) + { + System.out.println("MethodInliner: inlining ["+ + clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] in ["+ + targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); + } + + // Ignore the removal of the original method invocation, + // the addition of the parameter setup, and + // the modification of a few inlined instructions. + estimatedResultingCodeLength += codeAttribute.u4codeLength; + + // Append instructions to store the parameters. + storeParameters(clazz, method); + + // 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(parameterSize+1); + + // 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)); + } + } + + // Put the 'this' reference in variable 0 (plus offset). + if (!isStatic) + { + codeAttributeComposer.appendInstruction(parameterSize, + new VariableInstruction(InstructionConstants.OP_ASTORE, variableOffset)); + } + + 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); + + // Copy the instructions. + codeAttribute.instructionsAccept(clazz, method, this); + + // Append a label just after the code. + codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); + + // Copy the exceptions. + codeAttribute.exceptionsAccept(clazz, method, exceptionInfoAdder); + + codeAttributeComposer.endCodeFragment(); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + codeAttributeComposer.appendInstruction(offset, instruction); + } + + + 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); + } + 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); + } + + + 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); + } + + + 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. + constantInstruction.constantIndex = + constantAdder.addConstant(clazz, constantInstruction.constantIndex); + } + + codeAttributeComposer.appendInstruction(offset, constantInstruction); + } + + + // 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 (// Don't inline methods that must be preserved. + !KeepMarker.isKept(programMethod) && + + // 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 method, except in an 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 its target class has at least the + // same version number as the source class, in order to avoid + // introducing incompatible constructs. + targetClass.u4version >= programClass.u4version && + + // Only inline the method if it doesn't invoke a super method, or if + // it is in the same class. + (!SuperInvocationMarker.invokesSuperMethods(programMethod) || + 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 a class with at most + // a subset of the initialized superclasses. + (programClass.equals(targetClass) || + initializedSuperClasses(targetClass).containsAll(initializedSuperClasses(programClass)))) + { boolean oldInlining = inlining; + inlining = true; + inliningMethods.push(programMethod); + + // Inline the method body. + programMethod.attributesAccept(programClass, this); + + // Update the optimization information of the target method. + MethodOptimizationInfo info = + MethodOptimizationInfo.getMethodOptimizationInfo(targetMethod); + if (info != null) + { + info.merge(MethodOptimizationInfo.getMethodOptimizationInfo(programMethod)); + } + + inlining = oldInlining; + inliningMethods.pop(); + } + else if (programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + uninitializedObjectCount--; + } + } + + + /** + * Returns the set of superclasses and interfaces that are initialized. + */ + private Set initializedSuperClasses(Clazz clazz) + { + Set set = new HashSet(); + + // Visit all superclasses and interfaces, collecting the ones that have + // static initializers. + clazz.hierarchyAccept(true, true, true, false, + new StaticInitializerContainingClassFilter( + new ClassCollector(set))); + + return set; + } +} diff --git a/src/proguard/optimize/peephole/NopRemover.java b/src/proguard/optimize/peephole/NopRemover.java new file mode 100644 index 000000000..9396c404c --- /dev/null +++ b/src/proguard/optimize/peephole/NopRemover.java @@ -0,0 +1,89 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.peephole; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.editor.CodeAttributeEditor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor removes all nop instructions that it encounters. + * + * @author Eric Lafortune + */ +public class NopRemover +extends SimplifiedVisitor +implements InstructionVisitor +{ + private final CodeAttributeEditor codeAttributeEditor; + private final InstructionVisitor extraInstructionVisitor; + + + /** + * Creates a new NopRemover. + * @param codeAttributeEditor a code editor that can be used for + * accumulating changes to the code. + */ + public NopRemover(CodeAttributeEditor codeAttributeEditor) + { + this(codeAttributeEditor, null); + } + + + /** + * Creates a new NopRemover. + * @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(CodeAttributeEditor codeAttributeEditor, + InstructionVisitor extraInstructionVisitor) + { + this.codeAttributeEditor = codeAttributeEditor; + this.extraInstructionVisitor = extraInstructionVisitor; + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + // Check if the instruction is a nop instruction. + if (simpleInstruction.opcode == InstructionConstants.OP_NOP && + !codeAttributeEditor.isModified(offset)) + { + codeAttributeEditor.deleteInstruction(offset); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + 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 000000000..2a602eeca --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/ReachableCodeMarker.java b/src/proguard/optimize/peephole/ReachableCodeMarker.java new file mode 100644 index 000000000..b6fcf1893 --- /dev/null +++ b/src/proguard/optimize/peephole/ReachableCodeMarker.java @@ -0,0 +1,262 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + +import java.util.Arrays; + +/** + * 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. + Arrays.fill(isReachable, 0, codeLength, 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/RetargetedInnerClassAttributeRemover.java b/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java new file mode 100644 index 000000000..a67c6fffa --- /dev/null +++ b/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java @@ -0,0 +1,170 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +import java.util.Arrays; + +/** + * This ClassVisitor removes InnerClasses and EnclosingMethod attributes in + * classes that are retargeted or that refer to classes that are retargeted. + * + * @see ClassMerger + * @author Eric Lafortune + */ +public class RetargetedInnerClassAttributeRemover +extends SimplifiedVisitor +implements ClassVisitor, + AttributeVisitor, + InnerClassesInfoVisitor, + ConstantVisitor +{ + private boolean retargeted; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + int attributesCount = programClass.u2attributesCount; + Attribute[] attributes = programClass.attributes; + + int newAtributesCount = 0; + + // Copy over all non-retargeted attributes. + for (int index = 0; index < attributesCount; index++) + { + Attribute attribute = attributes[index]; + + // Check if it's an InnerClasses or EnclosingMethod attribute in + // a retargeted class or referring to a retargeted class. + retargeted = false; + attribute.accept(programClass, this); + if (!retargeted) + { + attributes[newAtributesCount++] = attribute; + } + } + + // Clean up any remaining array elements. + Arrays.fill(attributes, newAtributesCount, attributesCount, null); + + // Update the number of attributes. + programClass.u2attributesCount = newAtributesCount; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Check whether the class itself is retargeted. + checkTarget(clazz); + + if (!retargeted) + { + // Check whether the referenced classes are retargeted. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + int classesCount = innerClassesAttribute.u2classesCount; + InnerClassesInfo[] classes = innerClassesAttribute.classes; + + int newClassesCount = 0; + + // Copy over all non-retargeted attributes. + for (int index = 0; index < classesCount; index++) + { + InnerClassesInfo classInfo = classes[index]; + + // Check if the outer class or inner class is a retargeted class. + retargeted = false; + classInfo.outerClassConstantAccept(clazz, this); + classInfo.innerClassConstantAccept(clazz, this); + if (!retargeted) + { + classes[newClassesCount++] = classInfo; + } + } + + // Clean up any remaining array elements. + Arrays.fill(classes, newClassesCount, classesCount, null); + + // Update the number of classes. + innerClassesAttribute.u2classesCount = newClassesCount; + + // Remove the attribute altogether if it's empty. + retargeted = newClassesCount == 0; + } + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + // Check whether the class itself is retargeted. + checkTarget(clazz); + + // Check whether the referenced class is retargeted. + checkTarget(enclosingMethodAttribute.referencedClass); + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + // Check whether the inner class or the outer class are retargeted. + innerClassesInfo.innerClassConstantAccept(clazz, this); + innerClassesInfo.outerClassConstantAccept(clazz, this); + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Check whether the referenced class is retargeted. + checkTarget(classConstant.referencedClass); + } + + + // Small utility methods. + + /** + * Sets the global return value to true if the given class is retargeted. + */ + private void checkTarget(Clazz clazz) + { + if (clazz != null && + ClassMerger.getTargetClass(clazz) != null) + { + retargeted = true; + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/peephole/TargetClassChanger.java b/src/proguard/optimize/peephole/TargetClassChanger.java new file mode 100644 index 000000000..f997e0337 --- /dev/null +++ b/src/proguard/optimize/peephole/TargetClassChanger.java @@ -0,0 +1,455 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +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 replaces references to classes and class members if the + * classes have targets that are intended to replace them. + * + * @see VerticalClassMerger + * @see ClassReferenceFixer + * @see MemberReferenceFixer + * @author Eric Lafortune + */ +public class TargetClassChanger +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private static final boolean DEBUG = false; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Change the references of the constant pool. + programClass.constantPoolEntriesAccept(this); + + // Change the references of the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Change the references of the attributes. + programClass.attributesAccept(this); + + // Is the class itself being retargeted? + Clazz targetClass = ClassMerger.getTargetClass(programClass); + if (targetClass != null) + { + // Restore the class name. 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 classes. + programClass.u2thisClass = + addNewClassConstant(programClass, + programClass.getName(), + programClass); + + // This class will loose all its interfaces. + programClass.u2interfacesCount = 0; + + // This class will loose all its subclasses. + programClass.subClasses = null; + } + else + { + // Remove interface classes that are pointing to this class. + int newInterfacesCount = 0; + for (int index = 0; index < programClass.u2interfacesCount; index++) + { + Clazz interfaceClass = programClass.getInterface(index); + if (!programClass.equals(interfaceClass)) + { + programClass.u2interfaces[newInterfacesCount++] = + programClass.u2interfaces[index]; + } + } + programClass.u2interfacesCount = newInterfacesCount; + + // Update the subclasses of the superclass and interfaces of the + // target class. + ConstantVisitor subclassAdder = + new ReferencedClassVisitor( + new SubclassFilter(programClass, + new SubclassAdder(programClass))); + + programClass.superClassConstantAccept(subclassAdder); + programClass.interfaceConstantsAccept(subclassAdder); + + // TODO: Maybe restore private method references. + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Change the references of the class members. + libraryClass.fieldsAccept(this); + libraryClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Change the referenced class. + programField.referencedClass = + updateReferencedClass(programField.referencedClass); + + // Change the references of the attributes. + programField.attributesAccept(programClass, this); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Change the referenced classes. + updateReferencedClasses(programMethod.referencedClasses); + + // Change the references of the attributes. + programMethod.attributesAccept(programClass, this); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + // Change the referenced class. + libraryField.referencedClass = + updateReferencedClass(libraryField.referencedClass); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + // Change the referenced classes. + updateReferencedClasses(libraryMethod.referencedClasses); + } + + + // 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; + Clazz newReferencedClass = updateReferencedClass(referencedClass); + if (referencedClass != newReferencedClass) + { + // Change the referenced class. + stringConstant.referencedClass = newReferencedClass; + + // Change the referenced class member, if applicable. + stringConstant.referencedMember = + updateReferencedMember(stringConstant.referencedMember, + stringConstant.getString(clazz), + null, + newReferencedClass); + } + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + Clazz referencedClass = refConstant.referencedClass; + Clazz newReferencedClass = updateReferencedClass(referencedClass); + if (referencedClass != newReferencedClass) + { + if (DEBUG) + { + System.out.println("TargetClassChanger:"); + System.out.println(" ["+clazz.getName()+"] changing reference from ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]"); + } + + // Change the referenced class. + refConstant.referencedClass = newReferencedClass; + + // Change the referenced class member. + refConstant.referencedMember = + updateReferencedMember(refConstant.referencedMember, + refConstant.getName(clazz), + refConstant.getType(clazz), + newReferencedClass); + + if (DEBUG) + { + System.out.println(" ["+clazz.getName()+"] to ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]"); + } + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Change the referenced class. + classConstant.referencedClass = + updateReferencedClass(classConstant.referencedClass); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Change the references of the attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Change the references of the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Change the references of the local variables. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Change the referenced classes. + updateReferencedClasses(signatureAttribute.referencedClasses); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Change the references of the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Change the references of the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Change the references of the annotation. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Change the referenced class. + localVariableInfo.referencedClass = + updateReferencedClass(localVariableInfo.referencedClass); + } + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Change the referenced classes. + updateReferencedClasses(localVariableTypeInfo.referencedClasses); + } + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Change the referenced classes. + updateReferencedClasses(annotation.referencedClasses); + + // Change the references of the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) + { + Clazz referencedClass = elementValue.referencedClass; + Clazz newReferencedClass = updateReferencedClass(referencedClass); + if (referencedClass != newReferencedClass) + { + // Change the referenced annotation class. + elementValue.referencedClass = newReferencedClass; + + // Change the referenced method. + elementValue.referencedMethod = + (Method)updateReferencedMember(elementValue.referencedMethod, + elementValue.getMethodName(clazz), + null, + newReferencedClass); + } + } + + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, enumConstantElementValue); + + // Change the referenced classes. + updateReferencedClasses(enumConstantElementValue.referencedClasses); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, classElementValue); + + // Change the referenced classes. + updateReferencedClasses(classElementValue.referencedClasses); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, annotationElementValue); + + // Change the references of the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, arrayElementValue); + + // Change the references of the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Updates the retargeted classes in the given array of classes. + */ + private void updateReferencedClasses(Clazz[] referencedClasses) + { + if (referencedClasses == null) + { + return; + } + + for (int index = 0; index < referencedClasses.length; index++) + { + referencedClasses[index] = + updateReferencedClass(referencedClasses[index]); + } + } + + + /** + * Returns the retargeted class of the given class. + */ + private Clazz updateReferencedClass(Clazz referencedClass) + { + if (referencedClass == null) + { + return null; + } + + Clazz targetClazz = ClassMerger.getTargetClass(referencedClass); + return targetClazz != null ? + targetClazz : + referencedClass; + } + + + /** + * Returns the retargeted class member of the given class member. + */ + private Member updateReferencedMember(Member referencedMember, + String name, + String type, + Clazz newReferencedClass) + { + if (referencedMember == null) + { + return null; + } + + return referencedMember instanceof Field ? + (Member)newReferencedClass.findField(name, type) : + (Member)newReferencedClass.findMethod(name, type); + } + + + /** + * Explicitly adds a new class constant for the given class in the given + * program class. + */ + private int addNewClassConstant(ProgramClass programClass, + String className, + Clazz referencedClass) + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + int nameIndex = + constantPoolEditor.addUtf8Constant(className); + + int classConstantIndex = + constantPoolEditor.addConstant(new ClassConstant(nameIndex, + referencedClass)); + return classConstantIndex; + } +} \ No newline at end of file diff --git a/src/proguard/optimize/peephole/UnreachableCodeRemover.java b/src/proguard/optimize/peephole/UnreachableCodeRemover.java new file mode 100644 index 000000000..570b3cace --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..8e7771683 --- /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-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..6c059447e --- /dev/null +++ b/src/proguard/optimize/peephole/VariableShrinker.java @@ -0,0 +1,129 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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) + { + // Compute the parameter size. + int parameterSize = + ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), + method.getAccessFlags()); + + // Get the total size of the local variable frame. + int maxLocals = codeAttribute.u2maxLocals; + + if (DEBUG) + { + System.out.println("VariableShrinker: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + System.out.println(" Parameter size = " + parameterSize); + System.out.println(" Max locals = " + maxLocals); + } + + // Figure out the local variables that are used by the code. + variableUsageMarker.visitCodeAttribute(clazz, method, codeAttribute); + + // Delete unused local variables from the local variable frame. + variableEditor.reset(maxLocals); + + for (int variableIndex = parameterSize; variableIndex < maxLocals; 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/optimize/peephole/VerticalClassMerger.java b/src/proguard/optimize/peephole/VerticalClassMerger.java new file mode 100644 index 000000000..825de94da --- /dev/null +++ b/src/proguard/optimize/peephole/VerticalClassMerger.java @@ -0,0 +1,88 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.ProgramClass; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor inlines the direct subclasses into the program classes + * that it visits, whenever possible. + * + * @see ClassMerger + * @author Eric Lafortune + */ +public class VerticalClassMerger +extends SimplifiedVisitor +implements ClassVisitor +{ + private final boolean allowAccessModification; + private final boolean mergeInterfacesAggressively; + private final ClassVisitor extraClassVisitor; + + + /** + * Creates a new VerticalClassMerger. + * @param allowAccessModification specifies whether the access modifiers + * of classes can be changed in order to + * merge them. + * @param mergeInterfacesAggressively specifies whether interfaces may + * be merged aggressively. + */ + public VerticalClassMerger(boolean allowAccessModification, + boolean mergeInterfacesAggressively) + { + this(allowAccessModification, mergeInterfacesAggressively, null); + } + + + /** + * Creates a new VerticalClassMerger. + * @param allowAccessModification specifies whether the access modifiers + * of classes can be changed in order to + * merge them. + * @param mergeInterfacesAggressively specifies whether interfaces may + * be merged aggressively. + * @param extraClassVisitor an optional extra visitor for all + * merged classes. + */ + public VerticalClassMerger(boolean allowAccessModification, + boolean mergeInterfacesAggressively, + ClassVisitor extraClassVisitor) + { + this.allowAccessModification = allowAccessModification; + this.mergeInterfacesAggressively = mergeInterfacesAggressively; + this.extraClassVisitor = extraClassVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + programClass.subclassesAccept(new ClassMerger(programClass, + allowAccessModification, + mergeInterfacesAggressively, + extraClassVisitor)); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/peephole/package.html b/src/proguard/optimize/peephole/package.html new file mode 100644 index 000000000..e0eeb5141 --- /dev/null +++ b/src/proguard/optimize/peephole/package.html @@ -0,0 +1,3 @@ + +This package contains visitors that perform various peephole optimizations. + diff --git a/src/proguard/package.html b/src/proguard/package.html new file mode 100644 index 000000000..986cad89c --- /dev/null +++ b/src/proguard/package.html @@ -0,0 +1,5 @@ + +This package contains the main ProGuard application. +ProGuard can read jar files, shrink and obfuscate them, and write out the +resulting jar file. + diff --git a/src/proguard/preverify/CodePreverifier.java b/src/proguard/preverify/CodePreverifier.java new file mode 100644 index 000000000..7c382593e --- /dev/null +++ b/src/proguard/preverify/CodePreverifier.java @@ -0,0 +1,623 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.*; +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 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 CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + + /** + * 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; + + int codeLength = codeAttribute.u4codeLength; + + // Evaluate the method. + //partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute); + + // We may have to remove unreachable code. + codeAttributeEditor.reset(codeLength); + + // Collect the stack map frames. + List stackMapFrameList = new ArrayList(); + + for (int offset = 0; offset < codeLength; offset++) + { + // Only store frames at the beginning of code blocks. + if (!partialEvaluator.isTraced(offset)) + { + // Mark the unreachable instruction for deletion. + codeAttributeEditor.deleteInstruction(offset); + } + else if (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 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. + new AttributesEditor(programClass, programMethod, codeAttribute, true).deleteAttribute(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 = + new ConstantPoolEditor(programClass).addUtf8Constant(stackMapAttributeName); + + // Add the new stack map (table) attribute to the code attribute. + new AttributesEditor(programClass, programMethod, codeAttribute, true).addAttribute(stackMapAttribute); + + if (DEBUG) + { + System.out.println("Preverifier ["+programClass.getName()+"."+programMethod.getName(programClass)+"]:"); + stackMapAttribute.accept(programClass, programMethod, codeAttribute, new ClassPrinter()); + } + } + + // Apply code modifications, deleting unreachable code. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + + + // 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, + index == 0, + 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, + false, + 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, + boolean isVariable0, + 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(); + } + + // Does the reference type have a single producer? + if (offset != PartialEvaluator.AT_METHOD_ENTRY) + { + InstructionOffsetValue producers = producerValue.instructionOffsetValue(); + if (producers.instructionOffsetCount() == 1) + { + int producerOffset = producers.instructionOffset(0); + + // Follow any dup or swap instructions. + while (producerOffset != PartialEvaluator.AT_METHOD_ENTRY && + isDupOrSwap(codeAttribute.code[producerOffset])) + { + producers = partialEvaluator.getStackBefore(producerOffset).getTopProducerValue(0).instructionOffsetValue(); + producerOffset = producers.minimumValue(); + } + + // Are we in an instance initialization method, + // before the super initialization, loading "this"? + if (partialEvaluator.isInitializer() && + offset <= partialEvaluator.superInitializationOffset() && + (isVariable0 || + producerOffset > PartialEvaluator.AT_METHOD_ENTRY && + codeAttribute.code[producerOffset] == InstructionConstants.OP_ALOAD_0)) + { + // It's an UninitializedThis type. + return VerificationTypeFactory.createUninitializedThisType(); + } + + // Is the reference type newly created and still + // uninitialized? + if (producerOffset > PartialEvaluator.AT_METHOD_ENTRY && + offset <= partialEvaluator.initializationOffset(producerOffset)) + { + // It's an Uninitialized type. + return 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 new ConstantPoolEditor(programClass).addClassConstant(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; + } + + + /** + * Returns whether the given instruction opcode represents a dup or swap + * instruction (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap). + */ + private boolean isDupOrSwap(int opcode) + { + return opcode >= InstructionConstants.OP_DUP && + opcode <= InstructionConstants.OP_SWAP; + } +} diff --git a/src/proguard/preverify/CodeSubroutineInliner.java b/src/proguard/preverify/CodeSubroutineInliner.java new file mode 100644 index 000000000..c1549a3a4 --- /dev/null +++ b/src/proguard/preverify/CodeSubroutineInliner.java @@ -0,0 +1,389 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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.*; +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 = System.getProperty("csi") != null; + //*/ + + + private final BranchTargetFinder branchTargetFinder = new BranchTargetFinder(); + private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(true, true); + + private ExceptionInfoVisitor subroutineExceptionInliner = this; + private int clipStart = 0; + private int clipEnd = Integer.MAX_VALUE; + + + // 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"); +// CodeAttributeComposer.DEBUG = DEBUG; + + // TODO: Remove this when the subroutine inliner has stabilized. + // Catch any unexpected exceptions from the actual visiting method. + try + { + // Process the code. + visitCodeAttribute0(clazz, method, codeAttribute); + } + catch (RuntimeException ex) + { + System.err.println("Unexpected error while inlining subroutines:"); + System.err.println(" Class = ["+clazz.getName()+"]"); + System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + + if (DEBUG) + { + method.accept(clazz, new ClassPrinter()); + } + + throw ex; + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute); + + // Don't bother if there aren't any subroutines anyway. + if (!branchTargetFinder.containsSubroutines()) + { + return; + } + + if (DEBUG) + { + System.out.println("SubroutineInliner: processing ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + } + + // Append the body of the code. + codeAttributeComposer.reset(); + codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); + + // Copy the non-subroutine instructions. + 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 original subroutine instruction "+instruction.toString(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, + subroutineExceptionInliner); + + if (DEBUG) + { + System.out.println(" Appending label after code at ["+offset+"]"); + } + + // Append a label just after the code. + codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); + + // End and update the code attribute. + codeAttributeComposer.endCodeFragment(); + codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); + } + + + /** + * 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; + int oldClipStart = clipStart; + int oldClipEnd = clipEnd; + + subroutineExceptionInliner = + new ExceptionExcludedOffsetFilter(subroutineInvocationOffset, + subroutineExceptionInliner); + clipStart = subroutineStart; + clipEnd = subroutineEnd; + + 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); + + // Inline the subroutine exceptions. + codeAttribute.exceptionsAccept(clazz, + method, + subroutineStart, + subroutineEnd, + subroutineExceptionInliner); + + // We can again inline exceptions that are applicable to this + // subroutine invocation. + subroutineExceptionInliner = oldSubroutineExceptionInliner; + clipStart = oldClipStart; + clipEnd = oldClipEnd; + + codeAttributeComposer.endCodeFragment(); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Append the instruction. + codeAttributeComposer.appendInstruction(offset, instruction); + } + + + 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); + + 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); + + codeAttributeComposer.appendInstruction(offset, replacementInstruction); + } + } + else + { + // Append the instruction. + codeAttributeComposer.appendInstruction(offset, branchInstruction); + } + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + int startPC = Math.max(exceptionInfo.u2startPC, clipStart); + int endPC = Math.min(exceptionInfo.u2endPC, clipEnd); + int handlerPC = exceptionInfo.u2handlerPC; + int catchType = exceptionInfo.u2catchType; + + // Exclude any subroutine invocations that jump out of the try block, + // by adding a try block before (and later on, after) each invocation. + for (int offset = startPC; offset < endPC; offset++) + { + if (branchTargetFinder.isSubroutineInvocation(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, offset); + int instructionLength = instruction.length(offset); + + // Is it a subroutine invocation? + if (!exceptionInfo.isApplicable(offset + ((BranchInstruction)instruction).branchOffset)) + { + if (DEBUG) + { + System.out.println(" Appending extra exception ["+startPC+" -> "+offset+"] -> "+handlerPC); + } + + // Append a try block that ends before the subroutine invocation. + codeAttributeComposer.appendException(new ExceptionInfo(startPC, + offset, + handlerPC, + catchType)); + + // The next try block will start after the subroutine invocation. + startPC = offset + instructionLength; + } + } + } + + if (DEBUG) + { + if (startPC == exceptionInfo.u2startPC && + endPC == exceptionInfo.u2endPC) + { + System.out.println(" Appending exception ["+startPC+" -> "+endPC+"] -> "+handlerPC); + } + else + { + System.out.println(" Appending clipped exception ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+"] ~> ["+startPC+" -> "+endPC+"] -> "+handlerPC); + } + } + + // Append the exception. Note that exceptions with empty try blocks + // are automatically ignored. + codeAttributeComposer.appendException(new ExceptionInfo(startPC, + endPC, + handlerPC, + catchType)); + } +} diff --git a/src/proguard/preverify/Preverifier.java b/src/proguard/preverify/Preverifier.java new file mode 100644 index 000000000..da9649b36 --- /dev/null +++ b/src/proguard/preverify/Preverifier.java @@ -0,0 +1,73 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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))); + + // Classes from Java 6 may optionally be preverified. + // Classes from Java 7 or higher must be preverified. + if (!configuration.microEdition) + { + preverifier = + new ClassVersionFilter(ClassConstants.INTERNAL_CLASS_VERSION_1_6, + preverifier); + } + + programClassPool.classesAccept(preverifier); + } +} diff --git a/src/proguard/preverify/SubroutineInliner.java b/src/proguard/preverify/SubroutineInliner.java new file mode 100644 index 000000000..e21c469a5 --- /dev/null +++ b/src/proguard/preverify/SubroutineInliner.java @@ -0,0 +1,73 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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, + inliner); + } + + programClassPool.classesAccept(inliner); + } +} diff --git a/src/proguard/retrace/MANIFEST.MF b/src/proguard/retrace/MANIFEST.MF new file mode 100644 index 000000000..42c9800c8 --- /dev/null +++ b/src/proguard/retrace/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: proguard.retrace.ReTrace +Class-Path: proguard.jar diff --git a/src/proguard/retrace/ReTrace.java b/src/proguard/retrace/ReTrace.java new file mode 100644 index 000000000..91ab2a794 --- /dev/null +++ b/src/proguard/retrace/ReTrace.java @@ -0,0 +1,749 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.retrace; + +import proguard.classfile.util.ClassUtil; +import proguard.obfuscate.*; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + + +/** + * Tool for de-obfuscating stack traces of applications that were obfuscated + * with ProGuard. + * + * @author Eric Lafortune + */ +public class ReTrace +implements MappingProcessor +{ + private static final String REGEX_OPTION = "-regex"; + private static final String VERBOSE_OPTION = "-verbose"; + + + public static final String STACK_TRACE_EXPRESSION = "(?:.*?\\bat\\s+%c.%m\\s*\\(.*?(?::%l)?\\)\\s*)|(?:(?:.*?[:\"]\\s+)?%c(?::.*)?)"; + + private static final String REGEX_CLASS = "\\b(?:[A-Za-z0-9_$]+\\.)*[A-Za-z0-9_$]+\\b"; + private static final String REGEX_CLASS_SLASH = "\\b(?:[A-Za-z0-9_$]+/)*[A-Za-z0-9_$]+\\b"; + private static final String REGEX_LINE_NUMBER = "\\b[0-9]+\\b"; + private static final String REGEX_TYPE = REGEX_CLASS + "(?:\\[\\])*"; + private static final String REGEX_MEMBER = "?"; + private static final String REGEX_ARGUMENTS = "(?:" + REGEX_TYPE + "(?:\\s*,\\s*" + REGEX_TYPE + ")*)?"; + + // The class settings. + private final String regularExpression; + private final boolean verbose; + private final File mappingFile; + private final File stackTraceFile; + + private Map classMap = new HashMap(); + private Map classFieldMap = new HashMap(); + private Map classMethodMap = new HashMap(); + + + /** + * Creates a new ReTrace object to process stack traces on the standard + * input, based on the given mapping file name. + * @param regularExpression the regular expression for parsing the lines in + * the stack trace. + * @param verbose specifies whether the de-obfuscated stack trace + * should be verbose. + * @param mappingFile the mapping file that was written out by + * ProGuard. + */ + public ReTrace(String regularExpression, + boolean verbose, + File mappingFile) + { + this(regularExpression, verbose, mappingFile, null); + } + + + /** + * Creates a new ReTrace object to process a stack trace from the given file, + * based on the given mapping file name. + * @param regularExpression the regular expression for parsing the lines in + * the stack trace. + * @param verbose specifies whether the de-obfuscated stack trace + * should be verbose. + * @param mappingFile the mapping file that was written out by + * ProGuard. + * @param stackTraceFile the optional name of the file that contains the + * stack trace. + */ + public ReTrace(String regularExpression, + boolean verbose, + File mappingFile, + File stackTraceFile) + { + this.regularExpression = regularExpression; + this.verbose = verbose; + this.mappingFile = mappingFile; + this.stackTraceFile = stackTraceFile; + } + + + /** + * Performs the subsequent ReTrace operations. + */ + public void execute() throws IOException + { + // Read the mapping file. + MappingReader mappingReader = new MappingReader(mappingFile); + mappingReader.pump(this); + + + StringBuffer expressionBuffer = new StringBuffer(regularExpression.length() + 32); + char[] expressionTypes = new char[32]; + int expressionTypeCount = 0; + int index = 0; + while (true) + { + int nextIndex = regularExpression.indexOf('%', index); + if (nextIndex < 0 || + nextIndex == regularExpression.length()-1 || + expressionTypeCount == expressionTypes.length) + { + break; + } + + expressionBuffer.append(regularExpression.substring(index, nextIndex)); + expressionBuffer.append('('); + + char expressionType = regularExpression.charAt(nextIndex + 1); + switch(expressionType) + { + case 'c': + expressionBuffer.append(REGEX_CLASS); + break; + + case 'C': + expressionBuffer.append(REGEX_CLASS_SLASH); + break; + + case 'l': + expressionBuffer.append(REGEX_LINE_NUMBER); + break; + + case 't': + expressionBuffer.append(REGEX_TYPE); + break; + + case 'f': + expressionBuffer.append(REGEX_MEMBER); + break; + + case 'm': + expressionBuffer.append(REGEX_MEMBER); + break; + + case 'a': + expressionBuffer.append(REGEX_ARGUMENTS); + break; + } + + expressionBuffer.append(')'); + + expressionTypes[expressionTypeCount++] = expressionType; + + index = nextIndex + 2; + } + + expressionBuffer.append(regularExpression.substring(index)); + + Pattern pattern = Pattern.compile(expressionBuffer.toString()); + + // Read the stack trace file. + LineNumberReader reader = + new LineNumberReader(stackTraceFile == null ? + (Reader)new InputStreamReader(System.in) : + (Reader)new BufferedReader(new FileReader(stackTraceFile))); + + + try + { + StringBuffer outLine = new StringBuffer(256); + List extraOutLines = new ArrayList(); + + String className = null; + + // Read the line in the stack trace. + while (true) + { + String line = reader.readLine(); + if (line == null) + { + break; + } + + Matcher matcher = pattern.matcher(line); + + if (matcher.matches()) + { + int lineNumber = 0; + String type = null; + String arguments = null; + + // Figure out a class name, line number, type, and + // arguments beforehand. + for (int expressionTypeIndex = 0; expressionTypeIndex < expressionTypeCount; expressionTypeIndex++) + { + int startIndex = matcher.start(expressionTypeIndex + 1); + if (startIndex >= 0) + { + String match = matcher.group(expressionTypeIndex + 1); + + char expressionType = expressionTypes[expressionTypeIndex]; + switch (expressionType) + { + case 'c': + className = originalClassName(match); + break; + + case 'C': + className = originalClassName(ClassUtil.externalClassName(match)); + break; + + case 'l': + lineNumber = Integer.parseInt(match); + break; + + case 't': + type = originalType(match); + break; + + case 'a': + arguments = originalArguments(match); + break; + } + } + } + + // Actually construct the output line. + int lineIndex = 0; + + outLine.setLength(0); + extraOutLines.clear(); + + for (int expressionTypeIndex = 0; expressionTypeIndex < expressionTypeCount; expressionTypeIndex++) + { + int startIndex = matcher.start(expressionTypeIndex + 1); + if (startIndex >= 0) + { + int endIndex = matcher.end(expressionTypeIndex + 1); + String match = matcher.group(expressionTypeIndex + 1); + + // Copy a literal piece of input line. + outLine.append(line.substring(lineIndex, startIndex)); + + char expressionType = expressionTypes[expressionTypeIndex]; + switch (expressionType) + { + case 'c': + className = originalClassName(match); + outLine.append(className); + break; + + case 'C': + className = originalClassName(ClassUtil.externalClassName(match)); + outLine.append(ClassUtil.internalClassName(className)); + break; + + case 'l': + lineNumber = Integer.parseInt(match); + outLine.append(match); + break; + + case 't': + type = originalType(match); + outLine.append(type); + break; + + case 'f': + originalFieldName(className, + match, + type, + outLine, + extraOutLines); + break; + + case 'm': + originalMethodName(className, + match, + lineNumber, + type, + arguments, + outLine, + extraOutLines); + break; + + case 'a': + arguments = originalArguments(match); + outLine.append(arguments); + break; + } + + // Skip the original element whose processed version + // has just been appended. + lineIndex = endIndex; + } + } + + // Copy the last literal piece of input line. + outLine.append(line.substring(lineIndex)); + + // Print out the main line. + System.out.println(outLine); + + // Print out any additional lines. + for (int extraLineIndex = 0; extraLineIndex < extraOutLines.size(); extraLineIndex++) + { + System.out.println(extraOutLines.get(extraLineIndex)); + } + } + else + { + // Print out the original line. + System.out.println(line); + } + } + } + catch (IOException ex) + { + throw new IOException("Can't read stack trace (" + ex.getMessage() + ")"); + } + finally + { + if (stackTraceFile != null) + { + try + { + reader.close(); + } + catch (IOException ex) + { + // This shouldn't happen. + } + } + } + } + + + /** + * Finds the original field name(s), appending the first one to the out + * line, and any additional alternatives to the extra lines. + */ + private void originalFieldName(String className, + String obfuscatedFieldName, + String type, + StringBuffer outLine, + List extraOutLines) + { + int extraIndent = -1; + + // Class name -> obfuscated field names. + Map fieldMap = (Map)classFieldMap.get(className); + if (fieldMap != null) + { + // Obfuscated field names -> fields. + Set fieldSet = (Set)fieldMap.get(obfuscatedFieldName); + if (fieldSet != null) + { + // Find all matching fields. + Iterator fieldInfoIterator = fieldSet.iterator(); + while (fieldInfoIterator.hasNext()) + { + FieldInfo fieldInfo = (FieldInfo)fieldInfoIterator.next(); + if (fieldInfo.matches(type)) + { + // Is this the first matching field? + if (extraIndent < 0) + { + extraIndent = outLine.length(); + + // Append the first original name. + if (verbose) + { + outLine.append(fieldInfo.type).append(' '); + } + outLine.append(fieldInfo.originalName); + } + else + { + // Create an additional line with the proper + // indentation. + StringBuffer extraBuffer = new StringBuffer(); + for (int counter = 0; counter < extraIndent; counter++) + { + extraBuffer.append(' '); + } + + // Append the alternative name. + if (verbose) + { + extraBuffer.append(fieldInfo.type).append(' '); + } + extraBuffer.append(fieldInfo.originalName); + + // Store the additional line. + extraOutLines.add(extraBuffer); + } + } + } + } + } + + // Just append the obfuscated name if we haven't found any matching + // fields. + if (extraIndent < 0) + { + outLine.append(obfuscatedFieldName); + } + } + + + /** + * Finds the original method name(s), appending the first one to the out + * line, and any additional alternatives to the extra lines. + */ + private void originalMethodName(String className, + String obfuscatedMethodName, + int lineNumber, + String type, + String arguments, + StringBuffer outLine, + List extraOutLines) + { + int extraIndent = -1; + + // Class name -> obfuscated method names. + Map methodMap = (Map)classMethodMap.get(className); + if (methodMap != null) + { + // Obfuscated method names -> methods. + Set methodSet = (Set)methodMap.get(obfuscatedMethodName); + if (methodSet != null) + { + // Find all matching methods. + Iterator methodInfoIterator = methodSet.iterator(); + while (methodInfoIterator.hasNext()) + { + MethodInfo methodInfo = (MethodInfo)methodInfoIterator.next(); + if (methodInfo.matches(lineNumber, type, arguments)) + { + // Is this the first matching method? + if (extraIndent < 0) + { + extraIndent = outLine.length(); + + // Append the first original name. + if (verbose) + { + outLine.append(methodInfo.type).append(' '); + } + outLine.append(methodInfo.originalName); + if (verbose) + { + outLine.append('(').append(methodInfo.arguments).append(')'); + } + } + else + { + // Create an additional line with the proper + // indentation. + StringBuffer extraBuffer = new StringBuffer(); + for (int counter = 0; counter < extraIndent; counter++) + { + extraBuffer.append(' '); + } + + // Append the alternative name. + if (verbose) + { + extraBuffer.append(methodInfo.type).append(' '); + } + extraBuffer.append(methodInfo.originalName); + if (verbose) + { + extraBuffer.append('(').append(methodInfo.arguments).append(')'); + } + + // Store the additional line. + extraOutLines.add(extraBuffer); + } + } + } + } + } + + // Just append the obfuscated name if we haven't found any matching + // methods. + if (extraIndent < 0) + { + outLine.append(obfuscatedMethodName); + } + } + + + /** + * Returns the original argument types. + */ + private String originalArguments(String obfuscatedArguments) + { + StringBuffer originalArguments = new StringBuffer(); + + int startIndex = 0; + while (true) + { + int endIndex = obfuscatedArguments.indexOf(',', startIndex); + if (endIndex < 0) + { + break; + } + + originalArguments.append(originalType(obfuscatedArguments.substring(startIndex, endIndex).trim())).append(','); + + startIndex = endIndex + 1; + } + + originalArguments.append(originalType(obfuscatedArguments.substring(startIndex).trim())); + + return originalArguments.toString(); + } + + + /** + * Returns the original type. + */ + private String originalType(String obfuscatedType) + { + int index = obfuscatedType.indexOf('['); + + return index >= 0 ? + originalClassName(obfuscatedType.substring(0, index)) + obfuscatedType.substring(index) : + originalClassName(obfuscatedType); + } + + + /** + * Returns the original class name. + */ + private String originalClassName(String obfuscatedClassName) + { + String originalClassName = (String)classMap.get(obfuscatedClassName); + + return originalClassName != null ? + originalClassName : + obfuscatedClassName; + } + + + // Implementations for MappingProcessor. + + public boolean processClassMapping(String className, String newClassName) + { + // Obfuscated class name -> original class name. + classMap.put(newClassName, className); + + return true; + } + + + public void processFieldMapping(String className, String fieldType, String fieldName, String newFieldName) + { + // Original class name -> obfuscated field names. + Map fieldMap = (Map)classFieldMap.get(className); + if (fieldMap == null) + { + fieldMap = new HashMap(); + classFieldMap.put(className, fieldMap); + } + + // Obfuscated field name -> fields. + Set fieldSet = (Set)fieldMap.get(newFieldName); + if (fieldSet == null) + { + fieldSet = new LinkedHashSet(); + fieldMap.put(newFieldName, fieldSet); + } + + // Add the field information. + fieldSet.add(new FieldInfo(fieldType, + fieldName)); + } + + + public void processMethodMapping(String className, int firstLineNumber, int lastLineNumber, String methodReturnType, String methodName, String methodArguments, String newMethodName) + { + // Original class name -> obfuscated method names. + Map methodMap = (Map)classMethodMap.get(className); + if (methodMap == null) + { + methodMap = new HashMap(); + classMethodMap.put(className, methodMap); + } + + // Obfuscated method name -> methods. + Set methodSet = (Set)methodMap.get(newMethodName); + if (methodSet == null) + { + methodSet = new LinkedHashSet(); + methodMap.put(newMethodName, methodSet); + } + + // Add the method information. + methodSet.add(new MethodInfo(firstLineNumber, + lastLineNumber, + methodReturnType, + methodArguments, + methodName)); + } + + + /** + * A field record. + */ + private static class FieldInfo + { + private String type; + private String originalName; + + + private FieldInfo(String type, String originalName) + { + this.type = type; + this.originalName = originalName; + } + + + private boolean matches(String type) + { + return + type == null || type.equals(this.type); + } + } + + + /** + * A method record. + */ + private static class MethodInfo + { + private int firstLineNumber; + private int lastLineNumber; + private String type; + private String arguments; + private String originalName; + + + private MethodInfo(int firstLineNumber, int lastLineNumber, String type, String arguments, String originalName) + { + this.firstLineNumber = firstLineNumber; + this.lastLineNumber = lastLineNumber; + this.type = type; + this.arguments = arguments; + this.originalName = originalName; + } + + + private boolean matches(int lineNumber, String type, String arguments) + { + return + (lineNumber == 0 || (firstLineNumber <= lineNumber && lineNumber <= lastLineNumber) || lastLineNumber == 0) && + (type == null || type.equals(this.type)) && + (arguments == null || arguments.equals(this.arguments)); + } + } + + + /** + * The main program for ReTrace. + */ + public static void main(String[] args) + { + if (args.length < 1) + { + System.err.println("Usage: java proguard.ReTrace [-verbose] []"); + System.exit(-1); + } + + String regularExpresssion = STACK_TRACE_EXPRESSION; + boolean verbose = false; + + int argumentIndex = 0; + while (argumentIndex < args.length) + { + String arg = args[argumentIndex]; + if (arg.equals(REGEX_OPTION)) + { + regularExpresssion = args[++argumentIndex]; + } + else if (arg.equals(VERBOSE_OPTION)) + { + verbose = true; + } + else + { + break; + } + + argumentIndex++; + } + + if (argumentIndex >= args.length) + { + System.err.println("Usage: java proguard.ReTrace [-regex ] [-verbose] []"); + System.exit(-1); + } + + File mappingFile = new File(args[argumentIndex++]); + File stackTraceFile = argumentIndex < args.length ? + new File(args[argumentIndex]) : + null; + + ReTrace reTrace = new ReTrace(regularExpresssion, verbose, mappingFile, stackTraceFile); + + try + { + // Execute ReTrace with its given settings. + reTrace.execute(); + } + catch (IOException ex) + { + if (verbose) + { + // Print a verbose stack trace. + ex.printStackTrace(); + } + else + { + // Print just the stack trace message. + System.err.println("Error: "+ex.getMessage()); + } + + System.exit(1); + } + + System.exit(0); + } +} diff --git a/src/proguard/retrace/package.html b/src/proguard/retrace/package.html new file mode 100644 index 000000000..a35ce214a --- /dev/null +++ b/src/proguard/retrace/package.html @@ -0,0 +1,4 @@ + +This package contains the main ReTrace application. +ReTrace can de-obfuscate stack traces of obfuscated programs. + diff --git a/src/proguard/shrink/AnnotationUsageMarker.java b/src/proguard/shrink/AnnotationUsageMarker.java new file mode 100644 index 000000000..b9051a05c --- /dev/null +++ b/src/proguard/shrink/AnnotationUsageMarker.java @@ -0,0 +1,327 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 allClassesUsed; + 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); + } + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + if (isReferencedMethodUsed(enumConstantElementValue)) + { + // Check the referenced classes. + allClassesUsed = true; + enumConstantElementValue.referencedClassesAccept(this); + + if (allClassesUsed) + { + // Mark the element value as being used. + usageMarker.markAsUsed(enumConstantElementValue); + + markConstant(clazz, enumConstantElementValue.u2elementNameIndex); + markConstant(clazz, enumConstantElementValue.u2typeNameIndex); + markConstant(clazz, enumConstantElementValue.u2constantNameIndex); + } + } + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + if (isReferencedMethodUsed(classElementValue)) + { + // Mark the element value as being used. + usageMarker.markAsUsed(classElementValue); + + markConstant(clazz, classElementValue.u2elementNameIndex); + markConstant(clazz, classElementValue.u2classInfoIndex); + + // Mark the referenced classes, since they can be retrieved from + // the annotation and then used. + // TODO: This could mark more annotation methods, affecting other annotations. + classElementValue.referencedClassesAccept(usageMarker); + } + } + + + 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); + } + + annotationUsed = oldAnnotationUsed; + } + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + if (isReferencedMethodUsed(arrayElementValue)) + { + // Check and mark the contained element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + + // Mark the element value as being used. + usageMarker.markAsUsed(arrayElementValue); + + markConstant(clazz, arrayElementValue.u2elementNameIndex); + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + usageMarker.markAsUsed(constant); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Is the class constant marked as being used? + if (!usageMarker.isUsed(classConstant)) + { + // Check the referenced class. + allClassesUsed = true; + classConstant.referencedClassAccept(this); + + // Is the referenced class marked as being used? + if (allClassesUsed) + { + // Mark the class constant and its Utf8 constant. + usageMarker.markAsUsed(classConstant); + + markConstant(clazz, classConstant.u2nameIndex); + } + } + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + allClassesUsed &= usageMarker.isUsed(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + methodUsed = usageMarker.isUsed(programMethod); + } + + + public void visitLibraryMethod(LibraryClass LibraryClass, LibraryMethod libraryMethod) + { + } + + + // 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. + allClassesUsed = true; + annotation.referencedClassAccept(this); + + return allClassesUsed; + } + + + /** + * 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/ClassShrinker.java b/src/proguard/shrink/ClassShrinker.java new file mode 100644 index 000000000..f590c6375 --- /dev/null +++ b/src/proguard/shrink/ClassShrinker.java @@ -0,0 +1,479 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.*; +import proguard.classfile.constant.*; +import proguard.classfile.editor.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +import java.util.Arrays; + +/** + * This ClassVisitor removes constant pool entries, class members, and other + * class elements 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. + int newConstantPoolCount = + 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 the references to the constant pool if it has shrunk. + if (newConstantPoolCount < programClass.u2constantPoolCount) + { + programClass.u2constantPoolCount = newConstantPoolCount; + + // Remap all constant pool references. + constantPoolRemapper.setConstantIndexMap(constantIndexMap); + constantPoolRemapper.visitProgramClass(programClass); + } + + // Remove the unused interfaces from the class signature. + programClass.attributesAccept(new SignatureShrinker()); + + // Compact the extra field pointing to the subclasses of this class. + programClass.subClasses = + shrinkToNewArray(programClass.subClasses); + } + + + 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 visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + // Shrink the array of BootstrapMethodInfo objects. + bootstrapMethodsAttribute.u2bootstrapMethodsCount = + shrinkArray(bootstrapMethodsAttribute.bootstrapMethods, + bootstrapMethodsAttribute.u2bootstrapMethodsCount); + } + + + 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); + + // Shrink the attributes themselves. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Shrink the local variable info array. + localVariableTableAttribute.u2localVariableTableLength = + shrinkArray(localVariableTableAttribute.localVariableTable, + localVariableTableAttribute.u2localVariableTableLength); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Shrink the local variable type info array. + localVariableTypeTableAttribute.u2localVariableTypeTableLength = + shrinkArray(localVariableTypeTableAttribute.localVariableTypeTable, + localVariableTypeTableAttribute.u2localVariableTypeTableLength); + } + + + 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); + } + + + /** + * This AttributeVisitor updates the Utf8 constants of class signatures, + * removing any unused interfaces. + */ + private class SignatureShrinker + extends SimplifiedVisitor + implements AttributeVisitor + { + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + Clazz[] referencedClasses = signatureAttribute.referencedClasses; + if (referencedClasses != null) + { + // Go over the generic definitions, superclass and implemented interfaces. + String signature = clazz.getString(signatureAttribute.u2signatureIndex); + + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(signature); + + StringBuffer newSignatureBuffer = new StringBuffer(); + + int referencedClassIndex = 0; + int newReferencedClassIndex = 0; + + while (internalTypeEnumeration.hasMoreTypes()) + { + // Consider the classes referenced by this signature. + String type = internalTypeEnumeration.nextType(); + int classCount = new DescriptorClassEnumeration(type).classCount(); + + Clazz referencedClass = referencedClasses[referencedClassIndex]; + if (referencedClass == null || + usageMarker.isUsed(referencedClass)) + { + // Append the superclass or interface. + newSignatureBuffer.append(type); + + // Copy the referenced classes. + for (int counter = 0; counter < classCount; counter++) + { + referencedClasses[newReferencedClassIndex++] = + referencedClasses[referencedClassIndex++]; + } + } + else + { + // Skip the referenced classes. + referencedClassIndex += classCount; + } + } + + if (newReferencedClassIndex < referencedClassIndex) + { + // Update the signature. + ((Utf8Constant)((ProgramClass)clazz).constantPool[signatureAttribute.u2signatureIndex]).setString(newSignatureBuffer.toString()); + + // Clear the unused entries. + while (newReferencedClassIndex < referencedClassIndex) + { + referencedClasses[newReferencedClassIndex++] = null; + } + } + } + } + } + + + // Implementations for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {} + + + 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. + Arrays.fill(constantPool, counter, length, 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. + Arrays.fill(array, counter, length, 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 any remaining array elements. + if (counter < length) + { + Arrays.fill(array, counter, length, null); + } + + return counter; + } +} diff --git a/src/proguard/shrink/InnerUsageMarker.java b/src/proguard/shrink/InnerUsageMarker.java new file mode 100644 index 000000000..6d77e81a1 --- /dev/null +++ b/src/proguard/shrink/InnerUsageMarker.java @@ -0,0 +1,174 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This AttributeVisitor recursively marks all necessary inner class information + * in the attributes that it visits. + * + * @see UsageMarker + * + * @author Eric Lafortune + */ +public class InnerUsageMarker +extends SimplifiedVisitor +implements AttributeVisitor, + InnerClassesInfoVisitor, + ConstantVisitor, + ClassVisitor +{ + private final UsageMarker usageMarker; + + // Fields acting as a return parameters for several methods. + private boolean attributeUsed; + private boolean classUsed; + + + /** + * Creates a new InnerUsageMarker. + * @param usageMarker the usage marker that is used to mark the classes + * and class members. + */ + public InnerUsageMarker(UsageMarker usageMarker) + { + this.usageMarker = usageMarker; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // 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(innerClassesAttribute); + + markConstant(clazz, innerClassesAttribute.u2attributeNameIndex); + } + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + boolean innerClassesInfoUsed = usageMarker.isUsed(innerClassesInfo); + + if (!innerClassesInfoUsed) + { + // Check if the inner class (if any) is marked as being used. + classUsed = true; + innerClassesInfo.innerClassConstantAccept(clazz, this); + innerClassesInfoUsed = classUsed; + + // Check if the outer class (if any) is marked as being used. + classUsed = true; + innerClassesInfo.outerClassConstantAccept(clazz, this); + innerClassesInfoUsed &= classUsed; + + // If both the inner class and the outer class are marked as being + // used, then mark this InnerClassesInfo as well. + if (innerClassesInfoUsed) + { + usageMarker.markAsUsed(innerClassesInfo); + + innerClassesInfo.innerNameConstantAccept(clazz, this); + } + } + + // The return value. + 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; + } + + + // Small utility methods. + + /** + * Marks the given constant pool entry of the given class. This includes + * visiting any other referenced constant pool entries. + */ + private void markConstant(Clazz clazz, int index) + { + clazz.constantPoolEntryAccept(index, this); + } +} diff --git a/src/proguard/shrink/InterfaceUsageMarker.java b/src/proguard/shrink/InterfaceUsageMarker.java new file mode 100644 index 000000000..240838ec4 --- /dev/null +++ b/src/proguard/shrink/InterfaceUsageMarker.java @@ -0,0 +1,152 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + + +/** + * This ClassVisitor recursively marks all interface + * classes that are being used in the visited class. + * + * @see UsageMarker + * + * @author Eric Lafortune + */ +public class InterfaceUsageMarker +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor +{ + private final UsageMarker usageMarker; + + // Fields acting as a return parameters for several methods. + private boolean used; + private boolean anyUsed; + + + /** + * Creates a new InterfaceUsageMarker. + * @param usageMarker the usage marker that is used to mark the classes + * and class members. + */ + public InterfaceUsageMarker(UsageMarker usageMarker) + { + this.usageMarker = usageMarker; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + boolean classUsed = usageMarker.isUsed(programClass); + boolean classPossiblyUsed = usageMarker.isPossiblyUsed(programClass); + + if (classUsed || classPossiblyUsed) + { + // Check if any interfaces are being used. + boolean oldAnyUsed = anyUsed; + anyUsed = false; + + programClass.interfaceConstantsAccept(this); + + classUsed |= anyUsed; + anyUsed = oldAnyUsed; + + // Is this an interface with a preliminary mark? + if (classPossiblyUsed) + { + // Should it be included now? + if (classUsed) + { + // At least one if this interface's interfaces is being used. + // Mark this interface as well. + usageMarker.markAsUsed(programClass); + + // Mark this interface's name. + programClass.thisClassConstantAccept(this); + + // Mark the superclass (java/lang/Object). + programClass.superClassConstantAccept(this); + } + else + { + // Unmark this interface, so we don't bother looking at it again. + usageMarker.markAsUnused(programClass); + } + } + } + + // The return value. + used = classUsed; + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // The return values. + used = true; + anyUsed = true; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + boolean classUsed = usageMarker.isUsed(classConstant); + + if (!classUsed) + { + // The ClassConstant isn't marked as being used yet. But maybe it + // should be included as an interface, so check the actual class. + classConstant.referencedClassAccept(this); + classUsed = used; + + if (classUsed) + { + // The class is being used. Mark the ClassConstant as being used + // as well. + usageMarker.markAsUsed(classConstant); + + clazz.constantPoolEntryAccept(classConstant.u2nameIndex, this); + } + } + + // The return values. + used = classUsed; + anyUsed |= classUsed; + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + if (!usageMarker.isUsed(utf8Constant)) + { + usageMarker.markAsUsed(utf8Constant); + } + } +} diff --git a/src/proguard/shrink/LocalVariableTypeUsageMarker.java b/src/proguard/shrink/LocalVariableTypeUsageMarker.java new file mode 100644 index 000000000..573d8f65c --- /dev/null +++ b/src/proguard/shrink/LocalVariableTypeUsageMarker.java @@ -0,0 +1,178 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.*; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This AttributeVisitor recursively marks all information that points to used + * classes, in the LocalVariableTable and LocalVariableTypeTable attributes that + * it visits. + * + * @see UsageMarker + * + * @author Eric Lafortune + */ +public class LocalVariableTypeUsageMarker +extends SimplifiedVisitor +implements AttributeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + ClassVisitor, + ConstantVisitor +{ + private final UsageMarker usageMarker; + + // Fields acting as a return parameters for several methods. + private boolean tableUsed; + private boolean variableInfoUsed; + + + /** + * Creates a new LocalVariableTypeUsageMarker. + * @param usageMarker the usage marker that is used to mark the classes + * and class members. + */ + public LocalVariableTypeUsageMarker(UsageMarker usageMarker) + { + this.usageMarker = usageMarker; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Check and mark the individual entries. + tableUsed = false; + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + + // Mark the table if any of the entries is marked. + if (tableUsed) + { + usageMarker.markAsUsed(localVariableTableAttribute); + + markConstant(clazz, localVariableTableAttribute.u2attributeNameIndex); + } + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Check and mark the individual entries. + tableUsed = false; + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + + // Mark the table if any of the entries is marked. + if (tableUsed) + { + usageMarker.markAsUsed(localVariableTypeTableAttribute); + + markConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex); + } + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Only keep the local variable info if all of its classes are used. + variableInfoUsed = true; + localVariableInfo.referencedClassAccept(this); + + if (variableInfoUsed) + { + // We got a positive used flag, so the local variable info is useful. + usageMarker.markAsUsed(localVariableInfo); + + markConstant(clazz, localVariableInfo.u2nameIndex); + markConstant(clazz, localVariableInfo.u2descriptorIndex); + + tableUsed = true; + } + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Only keep the local variable info if all of its classes are used. + variableInfoUsed = true; + localVariableTypeInfo.referencedClassesAccept(this); + + if (variableInfoUsed) + { + // We got a positive used flag, so the local variable info is useful. + usageMarker.markAsUsed(localVariableTypeInfo); + + markConstant(clazz, localVariableTypeInfo.u2nameIndex); + markConstant(clazz, localVariableTypeInfo.u2signatureIndex); + + tableUsed = true; + } + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + // Don't keep the local variable info if one of its classes is not used. + if (!usageMarker.isUsed(programClass)) + { + variableInfoUsed = false; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + usageMarker.markAsUsed(constant); + } + + + // Small utility methods. + + /** + * Marks the given constant pool entry of the given class. + */ + private void markConstant(Clazz clazz, int index) + { + clazz.constantPoolEntryAccept(index, this); + } +} diff --git a/src/proguard/shrink/ShortestUsageMark.java b/src/proguard/shrink/ShortestUsageMark.java new file mode 100644 index 000000000..e2df7fa3c --- /dev/null +++ b/src/proguard/shrink/ShortestUsageMark.java @@ -0,0 +1,183 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 class can be used as a mark when keeping classes, class members, and + * other elements. It can be certain or preliminary. It also contains additional + * information about the reasons why an element is being kept. + * + * @see ClassShrinker + * + * @author Eric Lafortune + */ +final class ShortestUsageMark +{ + private final boolean certain; + private final String reason; + private final int depth; + private Clazz clazz; + private Member member; + + + /** + * Creates a new certain ShortestUsageMark. + * @param reason the reason for this mark. + */ + public ShortestUsageMark(String reason) + { + this.certain = true; + this.reason = reason; + this.depth = 0; + } + + + /** + * Creates a new certain ShortestUsageMark. + * @param previousUsageMark the previous mark to which this one is linked. + * @param reason the reason for this mark. + * @param clazz the class causing this mark. + */ + public ShortestUsageMark(ShortestUsageMark previousUsageMark, + String reason, + int cost, + Clazz clazz) + { + this(previousUsageMark, reason, cost, clazz, null); + } + + + /** + * Creates a new certain ShortestUsageMark. + * @param previousUsageMark the previous mark to which this one is linked. + * @param reason the reason for this mark. + * @param clazz the class causing this mark. + * @param member the member in the above class causing this mark. + * @param cost the added cost of following this path. + */ + public ShortestUsageMark(ShortestUsageMark previousUsageMark, + String reason, + int cost, + Clazz clazz, + Member member) + { + this.certain = true; + this.reason = reason; + this.depth = previousUsageMark.depth + cost; + this.clazz = clazz; + this.member = member; + } + + + /** + * Creates a new ShortestUsageMark, based on another mark. + * @param otherUsageMark the other mark, whose properties will be copied. + * @param certain specifies whether this is a certain mark. + */ + public ShortestUsageMark(ShortestUsageMark otherUsageMark, + boolean certain) + { + this.certain = certain; + this.reason = otherUsageMark.reason; + this.depth = otherUsageMark.depth; + this.clazz = otherUsageMark.clazz; + this.member = otherUsageMark.member; + } + + + /** + * Returns whether this is a certain mark. + */ + public boolean isCertain() + { + return certain; + } + + + /** + * Returns the reason for this mark. + */ + public String getReason() + { + return reason; + } + + + /** + * Returns whether this mark has a shorter chain of reasons than the + * given mark. + */ + public boolean isShorter(ShortestUsageMark otherUsageMark) + { + return this.depth < otherUsageMark.depth; + } + + + /** + * Returns whether this is mark is caused by the given class. + */ + public boolean isCausedBy(Clazz clazz) + { + return clazz.equals(this.clazz); + } + + + /** + * Applies the given class visitor to this mark's class, if any, + * and if this mark doesn't have a member. + */ + public void acceptClassVisitor(ClassVisitor classVisitor) + { + if (clazz != null && + member == null) + { + clazz.accept(classVisitor); + } + } + + + /** + * Applies the given class visitor to this mark's member, if any. + */ + public void acceptMemberVisitor(MemberVisitor memberVisitor) + { + if (clazz != null && + member != null) + { + member.accept(clazz, memberVisitor); + } + } + + + // Implementations for Object. + + public String toString() + { + return "certain=" + certain + ", depth="+depth+": " + + reason + + (clazz != null ? clazz.getName() : "(none)") + ": " + + (member != null ? member.getName(clazz) : "(none)"); + } +} diff --git a/src/proguard/shrink/ShortestUsageMarker.java b/src/proguard/shrink/ShortestUsageMarker.java new file mode 100644 index 000000000..1ac6e7e96 --- /dev/null +++ b/src/proguard/shrink/ShortestUsageMarker.java @@ -0,0 +1,277 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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 ClassShrinker + * + * @author Eric Lafortune + */ +public class ShortestUsageMarker extends UsageMarker +{ + private static final ShortestUsageMark INITIAL_MARK = + new ShortestUsageMark("is kept by a directive in the configuration.\n\n"); + + + // A field acting as a parameter to the visitor methods. + private ShortestUsageMark currentUsageMark = INITIAL_MARK; + + // A utility object to check for recursive causes. + private final MyRecursiveCauseChecker recursiveCauseChecker = new MyRecursiveCauseChecker(); + + + // Overriding implementations for UsageMarker. + + protected void markProgramClassBody(ProgramClass programClass) + { + ShortestUsageMark previousUsageMark = currentUsageMark; + + currentUsageMark = new ShortestUsageMark(getShortestUsageMark(programClass), + "is extended by ", + 10000, + programClass); + + super.markProgramClassBody(programClass); + + currentUsageMark = previousUsageMark; + } + + + protected void markProgramFieldBody(ProgramClass programClass, ProgramField programField) + { + ShortestUsageMark previousUsageMark = currentUsageMark; + + currentUsageMark = new ShortestUsageMark(getShortestUsageMark(programField), + "is referenced by ", + 1, + programClass, + programField); + + super.markProgramFieldBody(programClass, programField); + + currentUsageMark = previousUsageMark; + } + + + protected void markProgramMethodBody(ProgramClass programClass, ProgramMethod programMethod) + { + ShortestUsageMark previousUsageMark = currentUsageMark; + + currentUsageMark = new ShortestUsageMark(getShortestUsageMark(programMethod), + "is invoked by ", + 1, + programClass, + programMethod); + + super.markProgramMethodBody(programClass, programMethod); + + currentUsageMark = previousUsageMark; + } + + + protected void markMethodHierarchy(Clazz clazz, Method method) + { + ShortestUsageMark previousUsageMark = currentUsageMark; + + currentUsageMark = new ShortestUsageMark(getShortestUsageMark(method), + "implements ", + 100, + clazz, + method); + + super.markMethodHierarchy(clazz, method); + + currentUsageMark = previousUsageMark; + } + + + // Small utility methods. + + protected void markAsUsed(VisitorAccepter visitorAccepter) + { + Object visitorInfo = visitorAccepter.getVisitorInfo(); + + ShortestUsageMark shortestUsageMark = + visitorInfo != null && + visitorInfo instanceof ShortestUsageMark && + !((ShortestUsageMark)visitorInfo).isCertain() && + !currentUsageMark.isShorter((ShortestUsageMark)visitorInfo) ? + new ShortestUsageMark((ShortestUsageMark)visitorInfo, true): + currentUsageMark; + + visitorAccepter.setVisitorInfo(shortestUsageMark); + } + + + protected boolean shouldBeMarkedAsUsed(VisitorAccepter visitorAccepter) + { + Object visitorInfo = visitorAccepter.getVisitorInfo(); + + return //!(visitorAccepter instanceof Clazz && + // isCausedBy(currentUsageMark, (Clazz)visitorAccepter)) && + (visitorInfo == null || + !(visitorInfo instanceof ShortestUsageMark) || + !((ShortestUsageMark)visitorInfo).isCertain() || + currentUsageMark.isShorter((ShortestUsageMark)visitorInfo)); + } + + + protected boolean isUsed(VisitorAccepter visitorAccepter) + { + Object visitorInfo = visitorAccepter.getVisitorInfo(); + + return visitorInfo != null && + visitorInfo instanceof ShortestUsageMark && + ((ShortestUsageMark)visitorInfo).isCertain(); + } + + + protected void markAsPossiblyUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(new ShortestUsageMark(currentUsageMark, false)); + } + + + protected boolean shouldBeMarkedAsPossiblyUsed(VisitorAccepter visitorAccepter) + { + Object visitorInfo = visitorAccepter.getVisitorInfo(); + + return visitorInfo == null || + !(visitorInfo instanceof ShortestUsageMark) || + (!((ShortestUsageMark)visitorInfo).isCertain() && + currentUsageMark.isShorter((ShortestUsageMark)visitorInfo)); + } + + + protected boolean isPossiblyUsed(VisitorAccepter visitorAccepter) + { + Object visitorInfo = visitorAccepter.getVisitorInfo(); + + return visitorInfo != null && + visitorInfo instanceof ShortestUsageMark && + !((ShortestUsageMark)visitorInfo).isCertain(); + } + + + protected ShortestUsageMark getShortestUsageMark(VisitorAccepter visitorAccepter) + { + Object visitorInfo = visitorAccepter.getVisitorInfo(); + + return (ShortestUsageMark)visitorInfo; + } + + + // Small utility methods. + + private boolean isCausedBy(ShortestUsageMark shortestUsageMark, + Clazz clazz) + { + return recursiveCauseChecker.check(shortestUsageMark, clazz); + } + + + private class MyRecursiveCauseChecker implements ClassVisitor, MemberVisitor + { + private Clazz checkClass; + private boolean isRecursing; + + + public boolean check(ShortestUsageMark shortestUsageMark, + Clazz clazz) + { + checkClass = clazz; + isRecursing = false; + + shortestUsageMark.acceptClassVisitor(this); + shortestUsageMark.acceptMemberVisitor(this); + + return isRecursing; + } + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + checkCause(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + checkCause(libraryClass); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + checkCause(programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + checkCause(programMethod); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + checkCause(libraryField); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + checkCause(libraryMethod); + } + + + // Small utility methods. + + private void checkCause(VisitorAccepter visitorAccepter) + { + if (ShortestUsageMarker.this.isUsed(visitorAccepter)) + { + ShortestUsageMark shortestUsageMark = ShortestUsageMarker.this.getShortestUsageMark(visitorAccepter); + + // Check the class of this mark, if any + isRecursing = shortestUsageMark.isCausedBy(checkClass); + + // Check the causing class or method, if still necessary. + if (!isRecursing) + { + shortestUsageMark.acceptClassVisitor(this); + shortestUsageMark.acceptMemberVisitor(this); + } + } + } + } +} diff --git a/src/proguard/shrink/ShortestUsagePrinter.java b/src/proguard/shrink/ShortestUsagePrinter.java new file mode 100644 index 000000000..8740b9f87 --- /dev/null +++ b/src/proguard/shrink/ShortestUsagePrinter.java @@ -0,0 +1,220 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +import java.io.PrintStream; + + +/** + * 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 +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor +{ + private final ShortestUsageMarker shortestUsageMarker; + private final boolean verbose; + private final PrintStream ps; + + + /** + * Creates a new UsagePrinter that prints verbosely to System.out. + * @param shortestUsageMarker the usage marker that was used to mark the + * classes and class members. + */ + public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker) + { + this(shortestUsageMarker, true); + } + + + /** + * Creates a new UsagePrinter that prints to the given stream. + * @param shortestUsageMarker the usage marker that was used to mark the + * classes and class members. + * @param verbose specifies whether the output should be verbose. + */ + public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker, + boolean verbose) + { + this(shortestUsageMarker, verbose, System.out); + } + + /** + * Creates a new UsagePrinter that prints to the given stream. + * @param shortestUsageMarker the usage marker that was used to mark the + * classes and class members. + * @param verbose specifies whether the output should be verbose. + * @param printStream the stream to which to print. + */ + public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker, + boolean verbose, + PrintStream printStream) + { + this.shortestUsageMarker = shortestUsageMarker; + this.verbose = verbose; + this.ps = printStream; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Print the name of this class. + ps.println(ClassUtil.externalClassName(programClass.getName())); + + // Print the reason for keeping this class. + printReason(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Print the name of this class. + ps.println(ClassUtil.externalClassName(libraryClass.getName())); + + // Print the reason for keeping this class. + ps.println(" is a library class.\n"); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Print the name of this field. + String name = programField.getName(programClass); + String type = programField.getDescriptor(programClass); + + ps.println(ClassUtil.externalClassName(programClass.getName()) + + (verbose ? + ": " + ClassUtil.externalFullFieldDescription(0, name, type): + "." + name)); + + // Print the reason for keeping this method. + printReason(programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Print the name of this method. + String name = programMethod.getName(programClass); + String type = programMethod.getDescriptor(programClass); + + ps.print(ClassUtil.externalClassName(programClass.getName()) + + (verbose ? + ": " + ClassUtil.externalFullMethodDescription(programClass.getName(), 0, name, type): + "." + name)); + programMethod.attributesAccept(programClass, this); + ps.println(); + + // Print the reason for keeping this method. + printReason(programMethod); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + // Print the name of this field. + String name = libraryField.getName(libraryClass); + String type = libraryField.getDescriptor(libraryClass); + + ps.println(ClassUtil.externalClassName(libraryClass.getName()) + + (verbose ? + ": " + ClassUtil.externalFullFieldDescription(0, name, type): + "." + name)); + + // Print the reason for keeping this field. + ps.println(" is a library field.\n"); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + // Print the name of this method. + String name = libraryMethod.getName(libraryClass); + String type = libraryMethod.getDescriptor(libraryClass); + + ps.println(ClassUtil.externalClassName(libraryClass.getName()) + + (verbose ? + ": " + ClassUtil.externalFullMethodDescription(libraryClass.getName(), 0, name, type): + "." + name)); + + // Print the reason for keeping this method. + ps.println(" is a library method.\n"); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + ps.print(" (" + + lineNumberTableAttribute.getLowestLineNumber() + ":" + + lineNumberTableAttribute.getHighestLineNumber() + ")"); + } + + + // Small utility methods. + + private void printReason(VisitorAccepter visitorAccepter) + { + if (shortestUsageMarker.isUsed(visitorAccepter)) + { + ShortestUsageMark shortestUsageMark = shortestUsageMarker.getShortestUsageMark(visitorAccepter); + + // Print the reason for keeping this class. + ps.print(" " + shortestUsageMark.getReason()); + + // Print the class or method that is responsible, with its reasons. + shortestUsageMark.acceptClassVisitor(this); + shortestUsageMark.acceptMemberVisitor(this); + } + else + { + ps.println(" is not being kept.\n"); + } + } +} diff --git a/src/proguard/shrink/Shrinker.java b/src/proguard/shrink/Shrinker.java new file mode 100644 index 000000000..0472c3d4d --- /dev/null +++ b/src/proguard/shrink/Shrinker.java @@ -0,0 +1,179 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.shrink; + +import proguard.*; +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.visitor.*; + +import java.io.*; + +/** + * This class shrinks class pools according to a given configuration. + * + * @author Eric Lafortune + */ +public class Shrinker +{ + private final Configuration configuration; + + + /** + * Creates a new Shrinker. + */ + public Shrinker(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Performs shrinking of the given program class pool. + */ + 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.classesAccept(new ClassCleaner()); + libraryClassPool.classesAccept(new ClassCleaner()); + + // Create a visitor for marking the seeds. + UsageMarker usageMarker = configuration.whyAreYouKeeping == null ? + new UsageMarker() : + new ShortestUsageMarker(); + + // Automatically mark the parameterless constructors of seed classes, + // mainly for convenience and for backward compatibility. + ClassVisitor classUsageMarker = + new MultiClassVisitor(new ClassVisitor[] + { + usageMarker, + new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_INIT, + ClassConstants.INTERNAL_METHOD_TYPE_INIT, + usageMarker) + }); + + ClassPoolVisitor classPoolvisitor = + ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep, + classUsageMarker, + usageMarker, + true, + false, + false); + // Mark the seeds. + programClassPool.accept(classPoolvisitor); + libraryClassPool.accept(classPoolvisitor); + + // Mark interfaces that have to be kept. + programClassPool.classesAccept(new InterfaceUsageMarker(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), + new SignatureUsageMarker(usageMarker), + new LocalVariableTypeUsageMarker(usageMarker) + })))); + + // Should we explain ourselves? + if (configuration.whyAreYouKeeping != null) + { + System.out.println(); + + // Create a visitor for explaining classes and class members. + ShortestUsagePrinter shortestUsagePrinter = + new ShortestUsagePrinter((ShortestUsageMarker)usageMarker, + configuration.verbose); + + ClassPoolVisitor whyClassPoolvisitor = + ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.whyAreYouKeeping, + shortestUsagePrinter, + shortestUsagePrinter); + + // Mark the seeds. + programClassPool.accept(whyClassPoolvisitor); + libraryClassPool.accept(whyClassPoolvisitor); + } + + if (configuration.printUsage != null) + { + PrintStream ps = + configuration.printUsage == Configuration.STD_OUT ? System.out : + new PrintStream( + new BufferedOutputStream( + new FileOutputStream(configuration.printUsage))); + + // Print out items that will be removed. + programClassPool.classesAcceptAlphabetically( + new UsagePrinter(usageMarker, true, ps)); + + if (ps == System.out) + { + ps.flush(); + } + else + { + ps.close(); + } + } + + // Discard unused program classes. + int originalProgramClassPoolSize = programClassPool.size(); + + ClassPool newProgramClassPool = new ClassPool(); + 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/SignatureUsageMarker.java b/src/proguard/shrink/SignatureUsageMarker.java new file mode 100644 index 000000000..9c5cd4d29 --- /dev/null +++ b/src/proguard/shrink/SignatureUsageMarker.java @@ -0,0 +1,117 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This AttributeVisitor recursively marks all Signature attributes that it + * visits and that point to used classes. + * + * @see UsageMarker + * + * @author Eric Lafortune + */ +public class SignatureUsageMarker +extends SimplifiedVisitor +implements AttributeVisitor, + ClassVisitor, + ConstantVisitor +{ + private final UsageMarker usageMarker; + + // Fields acting as a return parameters for several methods. + private boolean attributeUsed; + + + /** + * Creates a new SignatureUsageMarker. + * @param usageMarker the usage marker that is used to mark the classes + * and class members. + */ + public SignatureUsageMarker(UsageMarker usageMarker) + { + this.usageMarker = usageMarker; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Only keep the signature if all of its classes are used. + attributeUsed = true; + signatureAttribute.referencedClassesAccept(this); + + if (attributeUsed) + { + // We got a positive used flag, so the signature is useful. + usageMarker.markAsUsed(signatureAttribute); + + markConstant(clazz, signatureAttribute.u2attributeNameIndex); + markConstant(clazz, signatureAttribute.u2signatureIndex); + } + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + // Don't keep the signature if one of its classes is not used. + if (!usageMarker.isUsed(programClass)) + { + attributeUsed = false; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + usageMarker.markAsUsed(constant); + } + + + // Small utility methods. + + /** + * Marks the given constant pool entry of the given class. + */ + private void markConstant(Clazz clazz, int index) + { + clazz.constantPoolEntryAccept(index, this); + } +} diff --git a/src/proguard/shrink/UsageMarker.java b/src/proguard/shrink/UsageMarker.java new file mode 100644 index 000000000..51210b568 --- /dev/null +++ b/src/proguard/shrink/UsageMarker.java @@ -0,0 +1,1052 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.preverification.*; +import proguard.classfile.attribute.preverification.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + + +/** + * This ClassVisitor and MemberVisitor recursively marks all classes and class + * elements that are being used. + * + * @see ClassShrinker + * + * @author Eric Lafortune + */ +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 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 final MyInterfaceUsageMarker interfaceUsageMarker = new MyInterfaceUsageMarker(); + private final MyPossiblyUsedMemberUsageMarker possiblyUsedMemberUsageMarker = new MyPossiblyUsedMemberUsageMarker(); + private final MemberVisitor nonEmptyMethodUsageMarker = new AllAttributeVisitor( + new MyNonEmptyMethodUsageMarker()); + private final ConstantVisitor parameterlessConstructorMarker = new ConstantTagFilter(new int[] { ClassConstants.CONSTANT_String, ClassConstants.CONSTANT_Class }, + new ReferencedClassVisitor( + new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_INIT, + ClassConstants.INTERNAL_METHOD_TYPE_INIT, + this))); + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (shouldBeMarkedAsUsed(programClass)) + { + // Mark this class. + markAsUsed(programClass); + + markProgramClassBody(programClass); + } + } + + + protected void markProgramClassBody(ProgramClass programClass) + { + // Mark this class's name. + markConstant(programClass, programClass.u2thisClass); + + // Mark the superclass. + if (programClass.u2superClass != 0) + { + markConstant(programClass, programClass.u2superClass); + } + + // Give the interfaces preliminary marks. + programClass.hierarchyAccept(false, false, true, false, + interfaceUsageMarker); + + // Explicitly mark the method, if it's not empty. + programClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, + ClassConstants.INTERNAL_METHOD_TYPE_CLINIT, + nonEmptyMethodUsageMarker); + + // Process all class members that have already been marked as possibly used. + programClass.fieldsAccept(possiblyUsedMemberUsageMarker); + programClass.methodsAccept(possiblyUsedMemberUsageMarker); + + // Mark the attributes. + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (shouldBeMarkedAsUsed(libraryClass)) + { + 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. + Clazz superClass = libraryClass.superClass; + if (superClass != null) + { + superClass.accept(this); + } + + // Mark the interfaces. + Clazz[] interfaceClasses = libraryClass.interfaceClasses; + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + if (interfaceClasses[index] != null) + { + interfaceClasses[index].accept(this); + } + } + } + + // Mark all methods. + libraryClass.methodsAccept(this); + } + } + + + /** + * This ClassVisitor marks ProgramClass objects as possibly used, + * and it visits LibraryClass objects with its outer UsageMarker. + */ + private class MyInterfaceUsageMarker + implements ClassVisitor + { + public void visitProgramClass(ProgramClass programClass) + { + if (shouldBeMarkedAsPossiblyUsed(programClass)) + { + // We can't process the interface yet, because it might not + // be required. Give it a preliminary mark. + markAsPossiblyUsed(programClass); + } + } + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Make sure all library interface methods are marked. + UsageMarker.this.visitLibraryClass(libraryClass); + } + } + + + /** + * This MemberVisitor marks ProgramField and ProgramMethod objects that + * have already been marked as possibly used. + */ + private class MyPossiblyUsedMemberUsageMarker + extends SimplifiedVisitor + implements MemberVisitor + { + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Has the method already been referenced? + if (isPossiblyUsed(programField)) + { + markAsUsed(programField); + + // Mark the name and descriptor. + markConstant(programClass, programField.u2nameIndex); + markConstant(programClass, programField.u2descriptorIndex); + + // Mark the attributes. + programField.attributesAccept(programClass, UsageMarker.this); + + // Mark the classes referenced in the descriptor string. + programField.referencedClassesAccept(UsageMarker.this); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Has the method already been referenced? + if (isPossiblyUsed(programMethod)) + { + markAsUsed(programMethod); + + // Mark the method body. + markProgramMethodBody(programClass, programMethod); + + // Note that, if the method has been marked as possibly used, + // the method hierarchy has already been marked (cfr. below). + } + } + } + + + /** + * This AttributeVisitor marks ProgramMethod objects of non-empty methods. + */ + private class MyNonEmptyMethodUsageMarker + 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.u4codeLength > 1) + { + method.accept(clazz, UsageMarker.this); + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (shouldBeMarkedAsUsed(programField)) + { + // Is the field's class used? + if (isUsed(programClass)) + { + markAsUsed(programField); + + // Mark the field body. + markProgramFieldBody(programClass, programField); + } + + // Hasn't the field been marked as possibly being used yet? + else if (shouldBeMarkedAsPossiblyUsed(programField)) + { + // We can't process the field yet, because the class isn't + // marked as being used (yet). Give it a preliminary mark. + markAsPossiblyUsed(programField); + } + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (shouldBeMarkedAsUsed(programMethod)) + { + // Is the method's class used? + if (isUsed(programClass)) + { + markAsUsed(programMethod); + + // Mark the method body. + markProgramMethodBody(programClass, programMethod); + + // Mark the method hierarchy. + markMethodHierarchy(programClass, programMethod); + } + + // Hasn't the method been marked as possibly being used yet? + 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(programMethod); + + // Mark the method hierarchy. + markMethodHierarchy(programClass, programMethod); + } + } + } + + + public void visitLibraryField(LibraryClass programClass, LibraryField programField) {} + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (shouldBeMarkedAsUsed(libraryMethod)) + { + markAsUsed(libraryMethod); + + // Mark the method hierarchy. + markMethodHierarchy(libraryClass, libraryMethod); + } + } + + + protected void markProgramFieldBody(ProgramClass programClass, ProgramField programField) + { + // Mark the name and descriptor. + markConstant(programClass, programField.u2nameIndex); + markConstant(programClass, programField.u2descriptorIndex); + + // Mark the attributes. + programField.attributesAccept(programClass, this); + + // Mark the classes referenced in the descriptor string. + programField.referencedClassesAccept(this); + } + + + protected void markProgramMethodBody(ProgramClass programClass, ProgramMethod programMethod) + { + // Mark the name and descriptor. + markConstant(programClass, programMethod.u2nameIndex); + markConstant(programClass, programMethod.u2descriptorIndex); + + // Mark the attributes. + programMethod.attributesAccept(programClass, this); + + // Mark the classes referenced in the descriptor string. + programMethod.referencedClassesAccept(this); + } + + + /** + * Marks the hierarchy of implementing or overriding methods corresponding + * to the given method, if any. + */ + protected void markMethodHierarchy(Clazz clazz, Method method) + { + int accessFlags = method.getAccessFlags(); + if ((accessFlags & + (ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC)) == 0 && + !ClassUtil.isInitializer(method.getName(clazz))) + { + // We can skip private and static methods in the hierarchy, and + // also abstract methods, unless they might widen a current + // non-public access. + int requiredUnsetAccessFlags = + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_STATIC | + ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) == 0 ? 0 : + ClassConstants.INTERNAL_ACC_ABSTRACT); + + clazz.accept(new ConcreteClassDownTraveler( + new ClassHierarchyTraveler(true, true, false, true, + new NamedMethodVisitor(method.getName(clazz), + method.getDescriptor(clazz), + new MemberAccessFilter(0, requiredUnsetAccessFlags, + this))))); + } + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + if (shouldBeMarkedAsUsed(integerConstant)) + { + markAsUsed(integerConstant); + } + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + if (shouldBeMarkedAsUsed(longConstant)) + { + markAsUsed(longConstant); + } + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + if (shouldBeMarkedAsUsed(floatConstant)) + { + markAsUsed(floatConstant); + } + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + if (shouldBeMarkedAsUsed(doubleConstant)) + { + markAsUsed(doubleConstant); + } + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + if (shouldBeMarkedAsUsed(stringConstant)) + { + markAsUsed(stringConstant); + + markConstant(clazz, stringConstant.u2stringIndex); + + // Mark the referenced class and class member, if any. + stringConstant.referencedClassAccept(this); + stringConstant.referencedMemberAccept(this); + } + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + if (shouldBeMarkedAsUsed(utf8Constant)) + { + markAsUsed(utf8Constant); + } + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + if (shouldBeMarkedAsUsed(invokeDynamicConstant)) + { + markAsUsed(invokeDynamicConstant); + + markConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex); + + // Mark the bootstrap methods attribute. + clazz.attributesAccept(new MyBootStrapMethodUsageMarker(invokeDynamicConstant.u2bootstrapMethodAttributeIndex)); + } + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + if (shouldBeMarkedAsUsed(methodHandleConstant)) + { + markAsUsed(methodHandleConstant); + + markConstant(clazz, methodHandleConstant.u2referenceIndex); + } + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + if (shouldBeMarkedAsUsed(refConstant)) + { + markAsUsed(refConstant); + + markConstant(clazz, refConstant.u2classIndex); + markConstant(clazz, refConstant.u2nameAndTypeIndex); + + // When compiled with "-target 1.2" or higher, the class or + // interface actually containing the referenced class member may + // be higher up the hierarchy. Make sure it's marked, in case it + // isn't used elsewhere. + refConstant.referencedClassAccept(this); + + // Mark the referenced class member itself. + refConstant.referencedMemberAccept(this); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + if (shouldBeMarkedAsUsed(classConstant)) + { + markAsUsed(classConstant); + + markConstant(clazz, classConstant.u2nameIndex); + + // Mark the referenced class itself. + classConstant.referencedClassAccept(this); + } + } + + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + if (shouldBeMarkedAsUsed(methodTypeConstant)) + { + markAsUsed(methodTypeConstant); + + markConstant(clazz, methodTypeConstant.u2descriptorIndex); + } + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + if (shouldBeMarkedAsUsed(nameAndTypeConstant)) + { + markAsUsed(nameAndTypeConstant); + + markConstant(clazz, nameAndTypeConstant.u2nameIndex); + markConstant(clazz, nameAndTypeConstant.u2descriptorIndex); + } + } + + + /** + * This AttributeVisitor marks the bootstrap methods attributes, their + * method entries, their method handles, and their arguments. + */ + private class MyBootStrapMethodUsageMarker + extends SimplifiedVisitor + implements AttributeVisitor, + BootstrapMethodInfoVisitor + { + private int bootstrapMethodIndex; + + + private MyBootStrapMethodUsageMarker(int bootstrapMethodIndex) + { + this.bootstrapMethodIndex = bootstrapMethodIndex; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + if (shouldBeMarkedAsUsed(bootstrapMethodsAttribute)) + { + markAsUsed(bootstrapMethodsAttribute); + + markConstant(clazz, bootstrapMethodsAttribute.u2attributeNameIndex); + + bootstrapMethodsAttribute.bootstrapMethodEntryAccept(clazz, + bootstrapMethodIndex, + this); + } + } + + + // Implementations for BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + markAsUsed(bootstrapMethodInfo); + + markConstant(clazz, bootstrapMethodInfo.u2methodHandleIndex); + + // Mark the constant pool entries referenced by the arguments. + bootstrapMethodInfo.methodArgumentsAccept(clazz, UsageMarker.this); + } + } + + + // Implementations for AttributeVisitor. + // Note that attributes are typically only referenced once, so we don't + // test if they have been marked already. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + // This is the best we can do for unknown attributes. + markAsUsed(unknownAttribute); + + markConstant(clazz, unknownAttribute.u2attributeNameIndex); + } + + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + // Don't mark the attribute and its name here. We may mark it in + // MyBootStrapMethodsAttributeUsageMarker. + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + markAsUsed(sourceFileAttribute); + + markConstant(clazz, sourceFileAttribute.u2attributeNameIndex); + markConstant(clazz, sourceFileAttribute.u2sourceFileIndex); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + markAsUsed(sourceDirAttribute); + + markConstant(clazz, sourceDirAttribute.u2attributeNameIndex); + markConstant(clazz, sourceDirAttribute.u2sourceDirIndex); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Don't mark the attribute and its name yet. We may mark it later, in + // InnerUsageMarker. + //markAsUsed(innerClassesAttribute); + + //markConstant(clazz, innerClassesAttribute.u2attrNameIndex); + + // Do mark the outer class entries. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + markAsUsed(enclosingMethodAttribute); + + markConstant(clazz, enclosingMethodAttribute.u2attributeNameIndex); + markConstant(clazz, enclosingMethodAttribute.u2classIndex); + + if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) + { + markConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + markAsUsed(deprecatedAttribute); + + markConstant(clazz, deprecatedAttribute.u2attributeNameIndex); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + markAsUsed(syntheticAttribute); + + markConstant(clazz, syntheticAttribute.u2attributeNameIndex); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Don't mark the attribute and its contents yet. We may mark them later, + // in SignatureUsageMarker. + //markAsUsed(signatureAttribute); + // + //markConstant(clazz, signatureAttribute.u2attributeNameIndex); + //markConstant(clazz, signatureAttribute.u2signatureIndex); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + markAsUsed(constantValueAttribute); + + markConstant(clazz, constantValueAttribute.u2attributeNameIndex); + markConstant(clazz, constantValueAttribute.u2constantValueIndex); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + markAsUsed(exceptionsAttribute); + + markConstant(clazz, exceptionsAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the exceptions. + exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + markAsUsed(codeAttribute); + + markConstant(clazz, codeAttribute.u2attributeNameIndex); + + // 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 visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + markAsUsed(stackMapAttribute); + + markConstant(clazz, stackMapAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the stack map frames. + stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + markAsUsed(stackMapTableAttribute); + + markConstant(clazz, stackMapTableAttribute.u2attributeNameIndex); + + // Mark the constant pool entries referenced by the stack map frames. + stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + markAsUsed(lineNumberTableAttribute); + + markConstant(clazz, lineNumberTableAttribute.u2attributeNameIndex); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Don't mark the attribute and its contents yet. We may mark them later, + // in LocalVariableTypeUsageMarker. + //markAsUsed(localVariableTableAttribute); + // + //markConstant(clazz, localVariableTableAttribute.u2attributeNameIndex); + // + //// Mark the constant pool entries referenced by the local variables. + //localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Don't mark the attribute and its contents yet. We may mark them later, + // in LocalVariableTypeUsageMarker. + //markAsUsed(localVariableTypeTableAttribute); + // + //markConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex); + // + //// Mark the constant pool entries referenced by the local variable types. + //localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // 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 visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // 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 visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // 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(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + markAsUsed(exceptionInfo); + + if (exceptionInfo.u2catchType != 0) + { + markConstant(clazz, exceptionInfo.u2catchType); + } + } + + + // Implementations for InnerClassesInfoVisitor. + + 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.u2innerClassIndex != 0 && + clazz.getName().equals(clazz.getClassName(innerClassesInfo.u2innerClassIndex))) + { + markAsUsed(innerClassesInfo); + + innerClassesInfo.innerClassConstantAccept(clazz, this); + innerClassesInfo.outerClassConstantAccept(clazz, this); + innerClassesInfo.innerNameConstantAccept(clazz, this); + } + } + + + // 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) + { + // Mark the constant pool entries referenced by the verification types. + sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + // Mark the constant pool entries referenced by the verification types. + moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + // Mark the constant pool entries referenced by 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) + { + markConstant(clazz, objectType.u2classIndex); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + markConstant(clazz, localVariableInfo.u2nameIndex); + markConstant(clazz, localVariableInfo.u2descriptorIndex); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + 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 visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + markConstant(clazz, constantInstruction.constantIndex); + + // Also mark the parameterless constructor of the class, in case the + // string constant or class constant is being used in a Class.forName + // or a .class construct. + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, + parameterlessConstructorMarker); + } + + + // Small utility methods. + + /** + * Marks the given visitor accepter as being used. + */ + protected void markAsUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(USED); + } + + + /** + * Returns whether the given visitor accepter should still be marked as + * being used. + */ + protected boolean shouldBeMarkedAsUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() != USED; + } + + + /** + * Returns whether the given visitor accepter has been marked as being used. + */ + protected boolean isUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == USED; + } + + + /** + * Marks the given visitor accepter as possibly being used. + */ + protected void markAsPossiblyUsed(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(POSSIBLY_USED); + } + + + /** + * Returns whether the given visitor accepter should still be marked as + * possibly being used. + */ + protected boolean shouldBeMarkedAsPossiblyUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() != USED && + visitorAccepter.getVisitorInfo() != POSSIBLY_USED; + } + + + /** + * Returns whether the given visitor accepter has been marked as possibly + * being used. + */ + protected boolean isPossiblyUsed(VisitorAccepter visitorAccepter) + { + return visitorAccepter.getVisitorInfo() == POSSIBLY_USED; + } + + + /** + * Clears any usage marks from the given visitor accepter. + */ + protected void markAsUnused(VisitorAccepter visitorAccepter) + { + visitorAccepter.setVisitorInfo(null); + } + + + /** + * Marks the given constant pool entry of the given class. This includes + * visiting any referenced objects. + */ + private void markConstant(Clazz clazz, int index) + { + clazz.constantPoolEntryAccept(index, this); + } +} diff --git a/src/proguard/shrink/UsagePrinter.java b/src/proguard/shrink/UsagePrinter.java new file mode 100644 index 000000000..69df7faf2 --- /dev/null +++ b/src/proguard/shrink/UsagePrinter.java @@ -0,0 +1,185 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.visitor.AttributeVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +import java.io.PrintStream; + + +/** + * This ClassVisitor prints out the classes and class members that have been + * marked as being used (or not used). + * + * @see UsageMarker + * + * @author Eric Lafortune + */ +public class UsagePrinter +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor +{ + 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; + + + /** + * Creates a new UsagePrinter that prints to System.out. + * @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) + { + this(usageMarker, printUnusedItems, System.out); + } + + + /** + * 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 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, + PrintStream printStream) + { + this.usageMarker = usageMarker; + this.printUnusedItems = printUnusedItems; + this.ps = printStream; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (usageMarker.isUsed(programClass)) + { + if (printUnusedItems) + { + className = programClass.getName(); + + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + className = null; + } + else + { + ps.println(ClassUtil.externalClassName(programClass.getName())); + } + } + else + { + if (printUnusedItems) + { + ps.println(ClassUtil.externalClassName(programClass.getName())); + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (usageMarker.isUsed(programField) ^ printUnusedItems) + { + printClassNameHeader(); + + ps.println(" " + + ClassUtil.externalFullFieldDescription( + programField.getAccessFlags(), + programField.getName(programClass), + programField.getDescriptor(programClass))); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (usageMarker.isUsed(programMethod) ^ printUnusedItems) + { + printClassNameHeader(); + + ps.print("===="); + ps.print(" "); + programMethod.attributesAccept(programClass, this); + ps.println(ClassUtil.externalFullMethodDescription( + programClass.getName(), + programMethod.getAccessFlags(), + programMethod.getName(programClass), + programMethod.getDescriptor(programClass))); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + ps.print(lineNumberTableAttribute.getLowestLineNumber() + ":" + + lineNumberTableAttribute.getHighestLineNumber() + ":"); + } + + + // Small utility methods. + + /** + * Prints the class name field. The field is then cleared, so it is not + * printed again. + */ + private void printClassNameHeader() + { + if (className != null) + { + ps.println(ClassUtil.externalClassName(className) + ":"); + className = null; + } + } +} diff --git a/src/proguard/shrink/UsedClassFilter.java b/src/proguard/shrink/UsedClassFilter.java new file mode 100644 index 000000000..7630b0bbf --- /dev/null +++ b/src/proguard/shrink/UsedClassFilter.java @@ -0,0 +1,74 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..f1a9c7544 --- /dev/null +++ b/src/proguard/shrink/UsedMemberFilter.java @@ -0,0 +1,93 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.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 UsedMemberFilter. + * @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/shrink/package.html b/src/proguard/shrink/package.html new file mode 100644 index 000000000..897319880 --- /dev/null +++ b/src/proguard/shrink/package.html @@ -0,0 +1,3 @@ + +This package contains classes to perform shrinking of class files. + diff --git a/src/proguard/util/AndMatcher.java b/src/proguard/util/AndMatcher.java new file mode 100644 index 000000000..1aa372638 --- /dev/null +++ b/src/proguard/util/AndMatcher.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 matches both given StringMatcher + * instances. + * + * @author Eric Lafortune + */ +public class AndMatcher implements StringMatcher +{ + private final StringMatcher matcher1; + private final StringMatcher matcher2; + + + public AndMatcher(StringMatcher matcher1, StringMatcher matcher2) + { + this.matcher1 = matcher1; + this.matcher2 = matcher2; + } + + + // Implementations for StringMatcher. + + public boolean matches(String string) + { + return matcher1.matches(string) && + matcher2.matches(string); + } +} diff --git a/src/proguard/util/ArrayUtil.java b/src/proguard/util/ArrayUtil.java new file mode 100644 index 000000000..d78cf4a5f --- /dev/null +++ b/src/proguard/util/ArrayUtil.java @@ -0,0 +1,960 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.lang.reflect.Array; +import java.util.Arrays; + +/** + * This class contains utility methods operating on arrays. + */ +public class ArrayUtil +{ + /** + * Returns whether the elements of the two given arrays are the same. + * @param array1 the first array. + * @param array2 the second array. + * @param size the size of the arrays to be checked. + * @return whether the elements are the same. + */ + public static boolean equal(byte[] array1, byte[] array2, int size) + { + for (int index = 0; index < size; index++) + { + if (array1[index] != array2[index]) + { + return false; + } + } + + return true; + } + + + /** + * Returns whether the elements of the two given arrays are the same. + * @param array1 the first array. + * @param array2 the second array. + * @param size the size of the arrays to be checked. + * @return whether the elements are the same. + */ + public static boolean equal(short[] array1, short[] array2, int size) + { + for (int index = 0; index < size; index++) + { + if (array1[index] != array2[index]) + { + return false; + } + } + + return true; + } + + + /** + * Returns whether the elements of the two given arrays are the same. + * @param array1 the first array. + * @param array2 the second array. + * @param size the size of the arrays to be checked. + * @return whether the elements are the same. + */ + public static boolean equal(int[] array1, int[] array2, int size) + { + for (int index = 0; index < size; index++) + { + if (array1[index] != array2[index]) + { + return false; + } + } + + return true; + } + + + /** + * Returns whether the elements of the two given arrays are the same. + * @param array1 the first array. + * @param array2 the second array. + * @param size the size of the arrays to be checked. + * @return whether the elements are the same. + */ + public static boolean equal(Object[] array1, Object[] array2, int size) + { + for (int index = 0; index < size; index++) + { + if (!array1[index].equals(array2[index])) + { + return false; + } + } + + return true; + } + + + /** + * Returns a hash code for the elements of the given array. + * @param array the array. + * @param size the size of the array to be taken into account. + * @return a hash code. + */ + public static int hashCode(byte[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + hashCode ^= array[index]; + } + + return hashCode; + } + + + /** + * Returns a hash code for the elements of the given array. + * @param array the array. + * @param size the size of the array to be taken into account. + * @return a hash code. + */ + public static int hashCode(short[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + hashCode ^= array[index]; + } + + return hashCode; + } + + + /** + * Returns a hash code for the elements of the given array. + * @param array the array. + * @param size the size of the array to be taken into account. + * @return a hash code. + */ + public static int hashCode(int[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + hashCode ^= array[index]; + } + + return hashCode; + } + + + /** + * Returns a hash code for the elements of the given array. + * @param array the array. + * @param size the size of the array to be taken into account. + * @return a hash code. + */ + public static int hashCode(Object[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + hashCode ^= array[index].hashCode(); + } + + return hashCode; + } + + + /** + * Compares the elements of the two given arrays. + * @param array1 the first array. + * @param size1 the size of the first array. + * @param array2 the second array. + * @param size2 the size of the second array. + * @return 0 if all elements are the same, + * -1 if the first different element in the first array is smaller + * than the corresponding element in the second array, + * or 1 if it is larger. + */ + public static int compare(byte[] array1, int size1, + byte[] array2, int size2) + { + int minSize = Math.min(size1, size2); + + for (int index = 0; index < minSize; index++) + { + if (array1[index] < array2[index]) + { + return -1; + } + else if (array1[index] > array2[index]) + { + return 1; + } + } + + return size1 < size2 ? -1 : + size1 == size2 ? 0 : + 1; + } + + + /** + * Compares the elements of the two given arrays. + * @param array1 the first array. + * @param size1 the size of the first array. + * @param array2 the second array. + * @param size2 the size of the second array. + * @return 0 if all elements are the same, + * -1 if the first different element in the first array is smaller + * than the corresponding element in the second array, + * or 1 if it is larger. + */ + public static int compare(short[] array1, int size1, + short[] array2, int size2) + { + int minSize = Math.min(size1, size2); + + for (int index = 0; index < minSize; index++) + { + if (array1[index] < array2[index]) + { + return -1; + } + else if (array1[index] > array2[index]) + { + return 1; + } + } + + return size1 < size2 ? -1 : + size1 == size2 ? 0 : + 1; + } + + + /** + * Compares the elements of the two given arrays. + * @param array1 the first array. + * @param size1 the size of the first array. + * @param array2 the second array. + * @param size2 the size of the second array. + * @return 0 if all elements are the same, + * -1 if the first different element in the first array is smaller + * than the corresponding element in the second array, + * or 1 if it is larger. + */ + public static int compare(int[] array1, int size1, + int[] array2, int size2) + { + int minSize = Math.min(size1, size2); + + for (int index = 0; index < minSize; index++) + { + if (array1[index] < array2[index]) + { + return -1; + } + else if (array1[index] > array2[index]) + { + return 1; + } + } + + return size1 < size2 ? -1 : + size1 == size2 ? 0 : + 1; + } + + + /** + * Compares the elements of the two given arrays. + * @param array1 the first array. + * @param size1 the size of the first array. + * @param array2 the second array. + * @param size2 the size of the second array. + * @return 0 if all elements are the same, + * -1 if the first different element in the first array is smaller + * than the corresponding element in the second array, + * or 1 if it is larger. + */ + public static int compare(Comparable[] array1, int size1, + Comparable[] array2, int size2) + { + int minSize = Math.min(size1, size2); + + for (int index = 0; index < minSize; index++) + { + int comparison = ObjectUtil.compare(array1[index], array2[index]); + if (comparison != 0) + { + return comparison; + } + } + + return size1 < size2 ? -1 : + size1 == size2 ? 0 : + 1; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @return the original array, or a copy if it had to be extended. + */ + public static boolean[] extendArray(boolean[] array, int size) + { + // Reuse the existing array if possible. + if (array.length >= size) + { + return array; + } + + // Otherwise create and initialize a new array. + boolean[] newArray = new boolean[size]; + + System.arraycopy(array, 0, + newArray, 0, + array.length); + + return newArray; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @param initialValue the intial value of the elements. + * @return the original array, or a copy if it had to be + * extended. + */ + public static boolean[] ensureArraySize(boolean[] array, + int size, + boolean initialValue) + { + // Is the existing array large enough? + if (array.length >= size) + { + // Reinitialize the existing array. + Arrays.fill(array, 0, size, initialValue); + } + else + { + // Otherwise create and initialize a new array. + array = new boolean[size]; + + if (initialValue) + { + Arrays.fill(array, 0, size, initialValue); + } + } + + return array; + } + + + /** + * Adds the given element to the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static byte[] add(byte[] array, int size, byte element) + { + array = extendArray(array, size + 1); + + array[size] = element; + + return array; + } + + + /** + * Inserts the given element in the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param index the index at which the element is to be added. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static byte[] insert(byte[] array, int size, int index, byte element) + { + array = extendArray(array, size + 1); + + // Move the last part. + System.arraycopy(array, index, + array, index + 1, + size - index); + + array[index] = element; + + return array; + } + + + /** + * Removes the specified element from the given array. + * @param array the array. + * @param size the original size of the array. + * @param index the index of the element to be removed. + */ + public static void remove(byte[] array, int size, int index) + { + System.arraycopy(array, index + 1, + array, index, + array.length - index - 1); + + array[size - 1] = 0; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @return the original array, or a copy if it had to be extended. + */ + public static byte[] extendArray(byte[] array, int size) + { + // Reuse the existing array if possible. + if (array.length >= size) + { + return array; + } + + // Otherwise create and initialize a new array. + byte[] newArray = new byte[size]; + + System.arraycopy(array, 0, + newArray, 0, + array.length); + + return newArray; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @param initialValue the intial value of the elements. + * @return the original array, or a copy if it had to be + * extended. + */ + public static byte[] ensureArraySize(byte[] array, + int size, + byte initialValue) + { + // Is the existing array large enough? + if (array.length >= size) + { + // Reinitialize the existing array. + Arrays.fill(array, 0, size, initialValue); + } + else + { + // Otherwise create and initialize a new array. + array = new byte[size]; + + if (initialValue != 0) + { + Arrays.fill(array, 0, size, initialValue); + } + } + + return array; + } + + + /** + * Adds the given element to the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static short[] add(short[] array, int size, short element) + { + array = extendArray(array, size + 1); + + array[size] = element; + + return array; + } + + + /** + * Inserts the given element in the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param index the index at which the element is to be added. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static short[] insert(short[] array, int size, int index, short element) + { + array = extendArray(array, size + 1); + + // Move the last part. + System.arraycopy(array, index, + array, index + 1, + size - index); + + array[index] = element; + + return array; + } + + + /** + * Removes the specified element from the given array. + * @param array the array. + * @param size the original size of the array. + * @param index the index of the element to be removed. + */ + public static void remove(short[] array, int size, int index) + { + System.arraycopy(array, index + 1, + array, index, + array.length - index - 1); + + array[size - 1] = 0; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @return the original array, or a copy if it had to be extended. + */ + public static short[] extendArray(short[] array, int size) + { + // Reuse the existing array if possible. + if (array.length >= size) + { + return array; + } + + // Otherwise create and initialize a new array. + short[] newArray = new short[size]; + + System.arraycopy(array, 0, + newArray, 0, + array.length); + + return newArray; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @param initialValue the intial value of the elements. + * @return the original array, or a copy if it had to be + * extended. + */ + public static short[] ensureArraySize(short[] array, + int size, + short initialValue) + { + // Is the existing array large enough? + if (array.length >= size) + { + // Reinitialize the existing array. + Arrays.fill(array, 0, size, initialValue); + } + else + { + // Otherwise create and initialize a new array. + array = new short[size]; + + if (initialValue != 0) + { + Arrays.fill(array, 0, size, initialValue); + } + } + + return array; + } + + + /** + * Adds the given element to the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static int[] add(int[] array, int size, int element) + { + array = extendArray(array, size + 1); + + array[size] = element; + + return array; + } + + + /** + * Inserts the given element in the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param index the index at which the element is to be added. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static int[] insert(int[] array, int size, int index, int element) + { + array = extendArray(array, size + 1); + + // Move the last part. + System.arraycopy(array, index, + array, index + 1, + size - index); + + array[index] = element; + + return array; + } + + + /** + * Removes the specified element from the given array. + * @param array the array. + * @param size the original size of the array. + * @param index the index of the element to be removed. + */ + public static void remove(int[] array, int size, int index) + { + System.arraycopy(array, index + 1, + array, index, + array.length - index - 1); + + array[size - 1] = 0; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @return the original array, or a copy if it had to be extended. + */ + public static int[] extendArray(int[] array, int size) + { + // Reuse the existing array if possible. + if (array.length >= size) + { + return array; + } + + // Otherwise create and initialize a new array. + int[] newArray = new int[size]; + + System.arraycopy(array, 0, + newArray, 0, + array.length); + + return newArray; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @param initialValue the intial value of the elements. + * @return the original array, or a copy if it had to be + * extended. + */ + public static int[] ensureArraySize(int[] array, + int size, + int initialValue) + { + // Is the existing array large enough? + if (array.length >= size) + { + // Reinitialize the existing array. + Arrays.fill(array, 0, size, initialValue); + } + else + { + // Otherwise create and initialize a new array. + array = new int[size]; + + if (initialValue != 0) + { + Arrays.fill(array, 0, size, initialValue); + } + } + + return array; + } + + + /** + * Adds the given element to the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static long[] add(long[] array, int size, long element) + { + array = extendArray(array, size + 1); + + array[size] = element; + + return array; + } + + + /** + * Inserts the given element in the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param index the index at which the element is to be added. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static long[] insert(long[] array, int size, int index, long element) + { + array = extendArray(array, size + 1); + + // Move the last part. + System.arraycopy(array, index, + array, index + 1, + size - index); + + array[index] = element; + + return array; + } + + + /** + * Removes the specified element from the given array. + * @param array the array. + * @param size the original size of the array. + * @param index the index of the element to be removed. + */ + public static void remove(long[] array, int size, int index) + { + System.arraycopy(array, index + 1, + array, index, + array.length - index - 1); + + array[size - 1] = 0; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @return the original array, or a copy if it had to be extended. + */ + public static long[] extendArray(long[] array, int size) + { + // Reuse the existing array if possible. + if (array.length >= size) + { + return array; + } + + // Otherwise create and initialize a new array. + long[] newArray = new long[size]; + + System.arraycopy(array, 0, + newArray, 0, + array.length); + + return newArray; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @param initialValue the intial value of the elements. + * @return the original array, or a copy if it had to be + * extended. + */ + public static long[] ensureArraySize(long[] array, + int size, + long initialValue) + { + // Is the existing array large enough? + if (array.length >= size) + { + // Reinitialize the existing array. + Arrays.fill(array, 0, size, initialValue); + } + else + { + // Otherwise create and initialize a new array. + array = new long[size]; + + if (initialValue != 0L) + { + Arrays.fill(array, 0, size, initialValue); + } + } + + return array; + } + + + /** + * Adds the given element to the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static Object[] add(Object[] array, int size, Object element) + { + array = extendArray(array, size + 1); + + array[size] = element; + + return array; + } + + + /** + * Inserts the given element in the given array. + * The array is extended if necessary. + * @param array the array. + * @param size the original size of the array. + * @param index the index at which the element is to be added. + * @param element the element to be added. + * @return the original array, or a copy if it had to be extended. + */ + public static Object[] insert(Object[] array, int size, int index, Object element) + { + array = extendArray(array, size + 1); + + // Move the last part. + System.arraycopy(array, index, + array, index + 1, + size - index); + + array[index] = element; + + return array; + } + + + /** + * Removes the specified element from the given array. + * @param array the array. + * @param size the original size of the array. + * @param index the index of the element to be removed. + */ + public static void remove(Object[] array, int size, int index) + { + System.arraycopy(array, index + 1, + array, index, + array.length - index - 1); + + array[size - 1] = null; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @return the original array, or a copy if it had to be extended. + */ + public static Object[] extendArray(Object[] array, int size) + { + // Reuse the existing array if possible. + if (array.length >= size) + { + return array; + } + + // Otherwise create and initialize a new array. + Object[] newArray = (Object[])Array.newInstance(array.getClass().getComponentType(), size); + + System.arraycopy(array, 0, + newArray, 0, + array.length); + + return newArray; + } + + + /** + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @param initialValue the intial value of the elements. + * @return the original array, or a copy if it had to be + * extended. + */ + public static Object[] ensureArraySize(Object[] array, + int size, + Object initialValue) + { + // Is the existing array large enough? + if (array.length >= size) + { + // Reinitialize the existing array. + Arrays.fill(array, 0, size, initialValue); + } + else + { + // Otherwise create and initialize a new array. + array = (Object[])Array.newInstance(array.getClass().getComponentType(), size); + + if (initialValue != null) + { + Arrays.fill(array, 0, size, initialValue); + } + } + + return array; + } +} diff --git a/src/proguard/util/ClassNameParser.java b/src/proguard/util/ClassNameParser.java new file mode 100644 index 000000000..22a07035f --- /dev/null +++ b/src/proguard/util/ClassNameParser.java @@ -0,0 +1,216 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/util/ConstantMatcher.java b/src/proguard/util/ConstantMatcher.java new file mode 100644 index 000000000..8c0f1e18e --- /dev/null +++ b/src/proguard/util/ConstantMatcher.java @@ -0,0 +1,48 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 matches any string or no string at all. + * + * @author Eric Lafortune + */ +public class ConstantMatcher implements StringMatcher +{ + private boolean matches; + + + /** + * Creates a new ConstantMatcher that always returns the given result. + */ + public ConstantMatcher(boolean matches) + { + this.matches = matches; + } + + + // Implementations for StringMatcher. + + public boolean matches(String string) + { + return matches; + } +} \ No newline at end of file diff --git a/src/proguard/util/EmptyStringMatcher.java b/src/proguard/util/EmptyStringMatcher.java new file mode 100644 index 000000000..f07c6666c --- /dev/null +++ b/src/proguard/util/EmptyStringMatcher.java @@ -0,0 +1,36 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 are empty. + * + * @author Eric Lafortune + */ +public class EmptyStringMatcher implements StringMatcher +{ + // 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 new file mode 100644 index 000000000..eeb627ac5 --- /dev/null +++ b/src/proguard/util/ExtensionMatcher.java @@ -0,0 +1,63 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 end in a given extension, ignoring + * its case. + * + * @author Eric Lafortune + */ +public class ExtensionMatcher implements StringMatcher +{ + private final String extension; + + + /** + * Creates a new StringMatcher. + * @param extension the extension against which strings will be matched. + */ + public ExtensionMatcher(String extension) + { + this.extension = extension; + } + + + // Implementations for StringMatcher. + + public boolean matches(String string) + { + 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/FileNameParser.java b/src/proguard/util/FileNameParser.java new file mode 100644 index 000000000..9ec6d22eb --- /dev/null +++ b/src/proguard/util/FileNameParser.java @@ -0,0 +1,121 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/FixedStringMatcher.java b/src/proguard/util/FixedStringMatcher.java new file mode 100644 index 000000000..2f0227121 --- /dev/null +++ b/src/proguard/util/FixedStringMatcher.java @@ -0,0 +1,56 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 given fixed string + * and then match another optional given StringMatcher. + * + * @author Eric Lafortune + */ +public class FixedStringMatcher implements StringMatcher +{ + private final String fixedString; + private final StringMatcher nextMatcher; + + + public FixedStringMatcher(String fixedString) + { + this(fixedString, null); + } + + + public FixedStringMatcher(String fixedString, StringMatcher nextMatcher) + { + this.fixedString = fixedString; + this.nextMatcher = nextMatcher; + } + + + // Implementations for StringMatcher. + + public boolean matches(String string) + { + return string.startsWith(fixedString) && + (nextMatcher == null || + 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 000000000..e07bff07f --- /dev/null +++ b/src/proguard/util/ListMatcher.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 000000000..b3b45185d --- /dev/null +++ b/src/proguard/util/ListParser.java @@ -0,0 +1,137 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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; + + + /** + * Creates a new ListParser that parses individual elements in the + * comma-separated list with the given 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)); + } + + + /** + * Creates a StringMatcher for the given regular expression, which can + * be a list of optionally negated simple entries. + *

+ * An empty list results in a StringMatcher that matches any string. + */ + 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 != null ? listMatcher : new ConstantMatcher(true); + } + + + // Small utility methods. + + /** + * 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 new file mode 100644 index 000000000..18bdce216 --- /dev/null +++ b/src/proguard/util/ListUtil.java @@ -0,0 +1,180 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 class provides some utility methods for working with + * java.util.List objects. + * + * @author Eric Lafortune + */ +public class ListUtil +{ + /** + * Creates a comma-separated String from the given List of String objects. + */ + public static String commaSeparatedString(List list, boolean quoteStrings) + { + if (list == null) + { + return null; + } + + StringBuffer buffer = new StringBuffer(); + + for (int index = 0; index < list.size(); index++) + { + if (index > 0) + { + buffer.append(','); + } + + String string = (String)list.get(index); + + if (quoteStrings) + { + string = quotedString(string); + } + + buffer.append(string); + } + + return buffer.toString(); + } + + + /** + * Creates a List of String objects from the given comma-separated String. + */ + public static List commaSeparatedList(String string) + { + if (string == null) + { + return null; + } + + List list = new ArrayList(); + int index = 0; + while ((index = skipWhitespace(string, index)) < string.length()) + { + int nextIndex; + + // Do we have an opening quote? + if (string.charAt(index) == '\'') + { + // Parse a quoted string. + nextIndex = string.indexOf('\'', index + 1); + if (nextIndex < 0) + { + nextIndex = string.length(); + } + + list.add(string.substring(index + 1, nextIndex)); + } + else + { + // Parse a non-quoted string. + nextIndex = string.indexOf(',', index); + if (nextIndex < 0) + { + nextIndex = string.length(); + } + + String substring = string.substring(index, nextIndex).trim(); + if (substring.length() > 0) + { + list.add(substring); + } + } + + index = nextIndex + 1; + } + + return list; + } + + + /** + * Skips any whitespace characters. + */ + private static int skipWhitespace(String string, int index) + { + while (index < string.length() && + Character.isWhitespace(string.charAt(index))) + { + index++; + } + return index; + } + + + /** + * Returns a quoted version of the given string, if necessary. + */ + private static String quotedString(String string) + { + return string.length() == 0 || + string.indexOf(' ') >= 0 || + string.indexOf('@') >= 0 || + string.indexOf('{') >= 0 || + string.indexOf('}') >= 0 || + string.indexOf('(') >= 0 || + string.indexOf(')') >= 0 || + string.indexOf(':') >= 0 || + string.indexOf(';') >= 0 || + string.indexOf(',') >= 0 ? ("'" + string + "'") : + ( string ); + } + + + public static void main(String[] args) + { + if (args.length == 1) + { + System.out.println("Input string: ["+args[0]+"]"); + + List list = commaSeparatedList(args[0]); + + System.out.println("Resulting list:"); + for (int index = 0; index < list.size(); index++) + { + System.out.println("["+list.get(index)+"]"); + } + } + else + { + List list = Arrays.asList(args); + + System.out.println("Input list:"); + for (int index = 0; index < list.size(); index++) + { + System.out.println("["+list.get(index)+"]"); + } + + String string = commaSeparatedString(list, true); + + System.out.println("Resulting string: ["+string+"]"); + } + } +} diff --git a/src/proguard/util/NameParser.java b/src/proguard/util/NameParser.java new file mode 100644 index 000000000..f25d52e75 --- /dev/null +++ b/src/proguard/util/NameParser.java @@ -0,0 +1,106 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/NotMatcher.java b/src/proguard/util/NotMatcher.java new file mode 100644 index 000000000..af539d065 --- /dev/null +++ b/src/proguard/util/NotMatcher.java @@ -0,0 +1,46 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 does not match the given + * StringMatcher. + * + * @author Eric Lafortune + */ +public class NotMatcher implements StringMatcher +{ + private final StringMatcher matcher; + + + public NotMatcher(StringMatcher matcher) + { + this.matcher = matcher; + } + + + // Implementations for StringMatcher. + + public boolean matches(String string) + { + return !matcher.matches(string); + } +} diff --git a/src/proguard/util/ObjectUtil.java b/src/proguard/util/ObjectUtil.java new file mode 100644 index 000000000..c5de36a34 --- /dev/null +++ b/src/proguard/util/ObjectUtil.java @@ -0,0 +1,67 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 class contains utility methods operating on objects. + */ +public class ObjectUtil +{ + /** + * Returns whether the given objects are the same. + * @param object1 the first object, may be null. + * @param object2 the second object, may be null. + * @return whether the objects are the same. + */ + public static boolean equal(Object object1, Object object2) + { + return object1 == null ? + object2 == null : + object1.equals(object2); + } + + + /** + * Returns the hash code of the given object, or 0 if it is null. + * @param object the object, may be null. + * @return the hash code. + */ + public static int hashCode(Object object) + { + return object == null ? 0 : object.hashCode(); + } + + + /** + * Returns a comparison of the two given objects. + * @param object1 the first object, may be null. + * @param object2 the second object, may be null. + * @return -1, 0, or 1. + * @see Comparable#compareTo(Object) + */ + public static int compare(Comparable object1, Comparable object2) + { + return object1 == null ? + object2 == null ? 0 : -1 : + object2 == null ? 1 : object1.compareTo(object2); + } +} diff --git a/src/proguard/util/OrMatcher.java b/src/proguard/util/OrMatcher.java new file mode 100644 index 000000000..7ad85c48c --- /dev/null +++ b/src/proguard/util/OrMatcher.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 matches either of the given + * StringMatcher instances. + * + * @author Eric Lafortune + */ +public class OrMatcher implements StringMatcher +{ + private final StringMatcher matcher1; + private final StringMatcher matcher2; + + + public OrMatcher(StringMatcher matcher1, StringMatcher matcher2) + { + this.matcher1 = matcher1; + this.matcher2 = matcher2; + } + + + // Implementations for StringMatcher. + + public boolean matches(String string) + { + return matcher1.matches(string) || + matcher2.matches(string); + } +} diff --git a/src/proguard/util/SettableMatcher.java b/src/proguard/util/SettableMatcher.java new file mode 100644 index 000000000..8650ccc84 --- /dev/null +++ b/src/proguard/util/SettableMatcher.java @@ -0,0 +1,46 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 delegates to a another StringMatcher that can be set + * after this StringMatcher has been constructed. + * + * @author Eric Lafortune + */ +public class SettableMatcher implements StringMatcher +{ + private StringMatcher matcher; + + + public void setMatcher(StringMatcher matcher) + { + this.matcher = matcher; + } + + + // Implementations for StringMatcher. + + public boolean matches(String string) + { + return matcher.matches(string); + } +} diff --git a/src/proguard/util/StringMatcher.java b/src/proguard/util/StringMatcher.java new file mode 100644 index 000000000..146dbfce0 --- /dev/null +++ b/src/proguard/util/StringMatcher.java @@ -0,0 +1,38 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 interface provides a method to determine whether strings match a given + * criterion, which is specified by the implementation. + * + * @author Eric Lafortune + */ +public interface StringMatcher +{ + /** + * Checks whether the given string matches. + * @param string the string to match. + * @return a boolean indicating whether the string matches the criterion. + */ + public boolean matches(String string); +} diff --git a/src/proguard/util/StringParser.java b/src/proguard/util/StringParser.java new file mode 100644 index 000000000..39d04d53e --- /dev/null +++ b/src/proguard/util/StringParser.java @@ -0,0 +1,35 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 interface provides a method to create a SringMatcher for a given + * regular expression. + * + * @author Eric Lafortune + */ +public interface StringParser +{ + /** + * Creates a StringMatcher for the given regular expression. + */ + public StringMatcher parse(String regularExpression); +} diff --git a/src/proguard/util/VariableStringMatcher.java b/src/proguard/util/VariableStringMatcher.java new file mode 100644 index 000000000..5a07c2ac2 --- /dev/null +++ b/src/proguard/util/VariableStringMatcher.java @@ -0,0 +1,126 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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/util/package.html b/src/proguard/util/package.html new file mode 100644 index 000000000..cb14fdc0a --- /dev/null +++ b/src/proguard/util/package.html @@ -0,0 +1,3 @@ + +This package contains utility classes for regular expression matching,... + diff --git a/src/proguard/wtk/ProGuardObfuscator.java b/src/proguard/wtk/ProGuardObfuscator.java new file mode 100644 index 000000000..b6f9ace04 --- /dev/null +++ b/src/proguard/wtk/ProGuardObfuscator.java @@ -0,0 +1,142 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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.wtk; + +import com.sun.kvem.environment.Obfuscator; +import proguard.*; + +import java.io.*; + + +/** + * ProGuard plug-in for the J2ME Wireless Toolkit. + *

+ * In order to integrate this plug-in in the toolkit, you'll have to put the + * following lines in the file + * {j2mewtk.dir}/wtklib/Linux/ktools.properties or + * {j2mewtk.dir}\wtklib\Windows\ktools.properties (whichever is + * applicable). + *

+ *

+ * obfuscator.runner.class.name: proguard.wtk.ProGuardObfuscator
+ * obfuscator.runner.classpath: /usr/local/java/proguard1.6/lib/proguard.jar
+ * 
+ * Please make sure the class path is set correctly for your system. + * + * @author Eric Lafortune + */ +public class ProGuardObfuscator implements Obfuscator +{ + private static final String DEFAULT_CONFIGURATION = "default.pro"; + + + // Implementations for Obfuscator. + + public void createScriptFile(File jadFile, + File projectDir) + { + // We don't really need to create a script file; + // we'll just fill out all options in the run method. + } + + + public void run(File obfuscatedJarFile, + String wtkBinDir, + String wtkLibDir, + String jarFileName, + String projectDirName, + String classPath, + String emptyAPI) + throws IOException + { + // Create the ProGuard configuration. + Configuration configuration = new Configuration(); + + // Parse the default configuration file. + ConfigurationParser parser = new ConfigurationParser(this.getClass().getResource(DEFAULT_CONFIGURATION), + System.getProperties()); + + try + { + parser.parse(configuration); + + // Fill out the library class path. + configuration.libraryJars = classPath(classPath); + + // Fill out the program class path (input and output). + configuration.programJars = new ClassPath(); + configuration.programJars.add(new ClassPathEntry(new File(jarFileName), false)); + configuration.programJars.add(new ClassPathEntry(obfuscatedJarFile, true)); + + // 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); + + // Run ProGuard with these options. + ProGuard proGuard = new ProGuard(configuration); + proGuard.execute(); + + } + catch (ParseException ex) + { + throw new IOException(ex.getMessage()); + } + finally + { + parser.close(); + } + } + + + /** + * Converts the given class path String into a ClassPath object. + */ + private ClassPath classPath(String classPathString) + { + ClassPath classPath = new ClassPath(); + + String separator = System.getProperty("path.separator"); + + int index = 0; + while (index < classPathString.length()) + { + // Find the next separator, or the end of the String. + int next_index = classPathString.indexOf(separator, index); + if (next_index < 0) + { + next_index = classPathString.length(); + } + + // Create and add the found class path entry. + ClassPathEntry classPathEntry = + new ClassPathEntry(new File(classPathString.substring(index, next_index)), + false); + + classPath.add(classPathEntry); + + // Continue after the separator. + index = next_index + 1; + } + + return classPath; + } +} diff --git a/src/proguard/wtk/default.pro b/src/proguard/wtk/default.pro new file mode 100644 index 000000000..d31714f0d --- /dev/null +++ b/src/proguard/wtk/default.pro @@ -0,0 +1,114 @@ +-dontnote +-microedition +-mergeinterfacesaggressively +-overloadaggressively +-repackageclasses '' +-allowaccessmodification + +# Keep all extensions of javax.microedition.midlet.MIDlet. +-keep public class * extends javax.microedition.midlet.MIDlet + +# Keep all native class/method names. +-keepclasseswithmembernames class * { + native ; +} + +# Remove all invocations of System methods without side effects +# whose return values are not used. +-assumenosideeffects public class java.lang.System { + public static native long currentTimeMillis(); + static java.lang.Class getCallerClass(); + public static native int identityHashCode(java.lang.Object); + public static java.lang.SecurityManager getSecurityManager(); + public static java.util.Properties getProperties(); + public static java.lang.String getProperty(java.lang.String); + public static java.lang.String getenv(java.lang.String); + public static native java.lang.String mapLibraryName(java.lang.String); + public static java.lang.String getProperty(java.lang.String,java.lang.String); +} + +# 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 static java.lang.String copyValueOf(char[]); + public static java.lang.String copyValueOf(char[],int,int); + public static java.lang.String valueOf(boolean); + public static java.lang.String valueOf(char); + public static java.lang.String valueOf(char[]); + public static java.lang.String valueOf(char[],int,int); + public static java.lang.String valueOf(double); + public static java.lang.String valueOf(float); + public static java.lang.String valueOf(int); + public static java.lang.String valueOf(java.lang.Object); + public static java.lang.String valueOf(long); + public boolean contentEquals(java.lang.StringBuffer); + public boolean endsWith(java.lang.String); + public boolean equalsIgnoreCase(java.lang.String); + public boolean equals(java.lang.Object); + public boolean matches(java.lang.String); + public boolean regionMatches(boolean,int,java.lang.String,int,int); + public boolean regionMatches(int,java.lang.String,int,int); + public boolean startsWith(java.lang.String); + public boolean startsWith(java.lang.String,int); + public byte[] getBytes(); + public byte[] getBytes(java.lang.String); + public char charAt(int); + public char[] toCharArray(); + public int compareToIgnoreCase(java.lang.String); + public int compareTo(java.lang.Object); + public int compareTo(java.lang.String); + public int hashCode(); + public int indexOf(int); + public int indexOf(int,int); + public int indexOf(java.lang.String); + public int indexOf(java.lang.String,int); + public int lastIndexOf(int); + public int lastIndexOf(int,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.CharSequence subSequence(int,int); + public java.lang.String concat(java.lang.String); + public java.lang.String replaceAll(java.lang.String,java.lang.String); + public java.lang.String replace(char,char); + public java.lang.String replaceFirst(java.lang.String,java.lang.String); + public java.lang.String[] split(java.lang.String); + public java.lang.String[] split(java.lang.String,int); + public java.lang.String substring(int); + public java.lang.String substring(int,int); + public java.lang.String toLowerCase(); + public java.lang.String toLowerCase(java.util.Locale); + public java.lang.String toString(); + public java.lang.String toUpperCase(); + public java.lang.String toUpperCase(java.util.Locale); + public java.lang.String trim(); +} + + +# 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.String toString(); + public char charAt(int); + public int capacity(); + public int indexOf(java.lang.String,int); + public int lastIndexOf(java.lang.String); + public int lastIndexOf(java.lang.String,int); + public int length(); + public java.lang.String substring(int); + public java.lang.String substring(int,int); +} diff --git a/src/proguard/wtk/package.html b/src/proguard/wtk/package.html new file mode 100644 index 000000000..6efc6441d --- /dev/null +++ b/src/proguard/wtk/package.html @@ -0,0 +1,3 @@ + +This package contains the J2ME Wireless Toolkit plug-in for ProGuard. +