From b0db59bc59fca1fc3f0083dd2e354a71b544d77c Mon Sep 17 00:00:00 2001 From: ProGuard Date: Sun, 4 Feb 2018 23:34:00 +0100 Subject: [PATCH] Created ProGuard version 6.0. --- README | 19 +- annotations/build.gradle | 16 + annotations/build.sh | 12 + annotations/build.xml | 47 + annotations/makefile | 7 + annotations/pom.xml | 34 + .../src/proguard/annotation/Keep.java | 0 .../proguard/annotation/KeepApplication.java | 0 .../annotation/KeepClassMemberNames.java | 0 .../proguard/annotation/KeepClassMembers.java | 0 .../annotation/KeepGettersSetters.java | 0 .../annotation/KeepImplementations.java | 0 .../src/proguard/annotation/KeepName.java | 0 .../KeepPublicClassMemberNames.java | 0 .../annotation/KeepPublicClassMembers.java | 0 .../annotation/KeepPublicGettersSetters.java | 0 .../annotation/KeepPublicImplementations.java | 0 .../KeepPublicProtectedClassMemberNames.java | 0 .../KeepPublicProtectedClassMembers.java | 0 ant/build.gradle | 25 + ant/build.sh | 31 + ant/build.xml | 57 + ant/makefile | 15 + {buildscripts/maven/ant => ant}/pom.xml | 25 +- ant/settings.gradle | 1 + .../src}/proguard/ant/ClassPathElement.java | 2 +- .../ant/ClassSpecificationElement.java | 12 +- .../proguard/ant/ConfigurationElement.java | 2 +- .../src}/proguard/ant/ConfigurationTask.java | 88 +- .../src}/proguard/ant/FilterElement.java | 2 +- .../ant/KeepSpecificationElement.java | 11 +- .../ant/MemberSpecificationElement.java | 2 +- .../src}/proguard/ant/ProGuardTask.java | 48 +- {src => ant/src}/proguard/ant/package.html | 0 {src => ant/src}/proguard/ant/task.properties | 0 buildscripts/README | 38 +- buildscripts/build.gradle | 133 +- buildscripts/build.properties | 4 - buildscripts/build.sh | 120 +- buildscripts/build.xml | 207 +- buildscripts/functions.sh | 43 + buildscripts/gradle.properties | 1 + buildscripts/makefile | 111 +- buildscripts/maven/base/pom.xml | 72 - buildscripts/{maven => }/pom.xml | 19 +- buildscripts/settings.gradle | 12 + core/build.gradle | 20 + core/build.sh | 12 + core/build.xml | 48 + core/makefile | 8 + core/pom.xml | 41 + .../src/META-INF}/MANIFEST.MF | 0 .../src}/proguard/ArgumentWordReader.java | 4 +- .../proguard/AssumeNoSideEffectsChecker.java | 2 +- .../src}/proguard/ClassMemberChecker.java | 2 +- {src => core/src}/proguard/ClassPath.java | 2 +- .../src}/proguard/ClassPathEntry.java | 72 +- .../src}/proguard/ClassSpecification.java | 3 +- .../ClassSpecificationVisitorFactory.java | 674 +++ {src => core/src}/proguard/Configuration.java | 52 +- .../src}/proguard/ConfigurationChecker.java | 13 +- .../src}/proguard/ConfigurationConstants.java | 22 +- .../src}/proguard/ConfigurationParser.java | 547 +- .../src}/proguard/ConfigurationWriter.java | 123 +- .../src}/proguard/DataEntryReaderFactory.java | 106 +- core/src/proguard/DataEntryWriterFactory.java | 211 + .../src}/proguard/DescriptorKeepChecker.java | 14 +- .../src}/proguard/DuplicateClassPrinter.java | 2 +- .../src}/proguard/FileWordReader.java | 2 +- .../FullyQualifiedClassNameChecker.java | 7 +- {src => core/src}/proguard/GPL.java | 2 +- .../src}/proguard/GetAnnotationChecker.java | 2 +- .../proguard/GetEnclosingClassChecker.java | 2 +- .../proguard/GetEnclosingMethodChecker.java | 2 +- .../src}/proguard/GetSignatureChecker.java | 2 +- {src => core/src}/proguard/Initializer.java | 77 +- {src => core/src}/proguard/InputReader.java | 34 +- .../src}/proguard/KeepClassMemberChecker.java | 2 +- .../src}/proguard/KeepClassSpecification.java | 76 +- .../KeepClassSpecificationVisitorFactory.java | 293 + .../src}/proguard/LibraryKeepChecker.java | 26 +- .../src}/proguard/LineWordReader.java | 2 +- .../src}/proguard/MemberSpecification.java | 9 +- core/src/proguard/OutputWriter.java | 298 + .../src}/proguard/ParseException.java | 2 +- {src => core/src}/proguard/ProGuard.java | 214 +- {src => core/src}/proguard/SeedPrinter.java | 32 +- .../src}/proguard/SubclassedClassFilter.java | 2 +- {src => core/src}/proguard/Targeter.java | 2 +- .../src}/proguard/UpToDateChecker.java | 21 +- {src => core/src}/proguard/WordReader.java | 62 +- core/src/proguard/backport/Backporter.java | 231 + .../DefaultInterfaceMethodConverter.java | 304 + .../proguard/backport/LambdaExpression.java | 192 + .../backport/LambdaExpressionCollector.java | 235 + .../backport/LambdaExpressionConverter.java | 991 ++++ .../StaticInterfaceMethodConverter.java | 298 + .../StringConcatenationConverter.java | 314 + .../proguard/classfile/ClassConstants.java | 287 +- .../src}/proguard/classfile/ClassPool.java | 2 +- .../src}/proguard/classfile/Clazz.java | 2 +- .../src}/proguard/classfile/Field.java | 2 +- .../proguard/classfile/JavaConstants.java | 8 +- .../src}/proguard/classfile/LibraryClass.java | 2 +- .../src}/proguard/classfile/LibraryField.java | 2 +- .../proguard/classfile/LibraryMember.java | 2 +- .../proguard/classfile/LibraryMethod.java | 2 +- .../src}/proguard/classfile/Member.java | 2 +- .../src}/proguard/classfile/Method.java | 2 +- .../src}/proguard/classfile/ProgramClass.java | 82 +- .../src}/proguard/classfile/ProgramField.java | 22 +- .../proguard/classfile/ProgramMember.java | 2 +- .../proguard/classfile/ProgramMethod.java | 22 +- .../proguard/classfile/VisitorAccepter.java | 2 +- .../classfile/attribute/Attribute.java | 2 +- .../attribute/BootstrapMethodInfo.java | 2 +- .../attribute/BootstrapMethodsAttribute.java | 2 +- .../classfile/attribute/CodeAttribute.java | 42 +- .../attribute/ConstantValueAttribute.java | 2 +- .../attribute/DeprecatedAttribute.java | 2 +- .../attribute/EnclosingMethodAttribute.java | 2 +- .../classfile/attribute/ExceptionInfo.java | 5 +- .../attribute/ExceptionsAttribute.java | 2 +- .../attribute/ExtendedLineNumberInfo.java | 2 +- .../attribute/InnerClassesAttribute.java | 2 +- .../classfile/attribute/InnerClassesInfo.java | 27 +- .../classfile/attribute/LineNumberInfo.java | 2 +- .../attribute/LineNumberTableAttribute.java | 2 +- .../attribute/LocalVariableInfo.java | 2 +- .../LocalVariableTableAttribute.java | 2 +- .../attribute/LocalVariableTypeInfo.java | 2 +- .../LocalVariableTypeTableAttribute.java | 2 +- .../attribute/MethodParametersAttribute.java | 2 +- .../classfile/attribute/ParameterInfo.java | 16 +- .../attribute/SignatureAttribute.java | 2 +- .../attribute/SourceDirAttribute.java | 2 +- .../attribute/SourceFileAttribute.java | 2 +- .../attribute/SyntheticAttribute.java | 2 +- .../classfile/attribute/UnknownAttribute.java | 2 +- .../attribute/annotation/Annotation.java | 2 +- .../AnnotationDefaultAttribute.java | 2 +- .../annotation/AnnotationElementValue.java | 2 +- .../annotation/AnnotationsAttribute.java | 2 +- .../annotation/ArrayElementValue.java | 2 +- .../annotation/ClassElementValue.java | 2 +- .../annotation/ConstantElementValue.java | 2 +- .../attribute/annotation/ElementValue.java | 2 +- .../annotation/EnumConstantElementValue.java | 2 +- .../ParameterAnnotationsAttribute.java | 2 +- .../RuntimeInvisibleAnnotationsAttribute.java | 2 +- ...nvisibleParameterAnnotationsAttribute.java | 2 +- ...timeInvisibleTypeAnnotationsAttribute.java | 2 +- .../RuntimeVisibleAnnotationsAttribute.java | 2 +- ...eVisibleParameterAnnotationsAttribute.java | 2 +- ...untimeVisibleTypeAnnotationsAttribute.java | 2 +- .../attribute/annotation/TypeAnnotation.java | 2 +- .../annotation/TypeAnnotationsAttribute.java | 2 +- .../attribute/annotation/TypePathInfo.java | 2 +- .../attribute/annotation/package.html | 0 .../annotation/target/CatchTargetInfo.java | 2 +- .../annotation/target/EmptyTargetInfo.java | 2 +- .../target/FormalParameterTargetInfo.java | 2 +- .../target/LocalVariableTargetElement.java | 2 +- .../target/LocalVariableTargetInfo.java | 2 +- .../annotation/target/OffsetTargetInfo.java | 2 +- .../target/SuperTypeTargetInfo.java | 2 +- .../annotation/target/TargetInfo.java | 2 +- .../annotation/target/ThrowsTargetInfo.java | 2 +- .../target/TypeArgumentTargetInfo.java | 2 +- .../target/TypeParameterBoundTargetInfo.java | 2 +- .../target/TypeParameterTargetInfo.java | 2 +- .../LocalVariableTargetElementVisitor.java | 2 +- .../target/visitor/TargetInfoVisitor.java | 2 +- .../visitor/AllAnnotationVisitor.java | 2 +- .../visitor/AllElementValueVisitor.java | 2 +- .../AnnotationToAnnotatedClassVisitor.java | 6 +- .../AnnotationToAnnotatedMemberVisitor.java | 6 +- .../visitor/AnnotationTypeFilter.java | 49 +- .../annotation/visitor/AnnotationVisitor.java | 2 +- .../visitor/ElementValueVisitor.java | 2 +- .../visitor/TypeAnnotationVisitor.java | 2 +- .../visitor/TypePathInfoVisitor.java | 2 +- .../attribute/annotation/visitor/package.html | 0 .../attribute/module/ExportsInfo.java | 106 + .../attribute/module/ModuleAttribute.java | 195 + .../module/ModuleMainClassAttribute.java | 76 + .../module/ModulePackagesAttribute.java | 79 + .../classfile/attribute/module/OpensInfo.java | 106 + .../attribute/module/ProvidesInfo.java | 103 + .../attribute/module/RequiresInfo.java | 102 + .../classfile/attribute/module/package.html | 4 + .../module/visitor/AllExportsInfoVisitor.java | 57 + .../module/visitor/AllOpensInfoVisitor.java | 57 + .../visitor/AllProvidesInfoVisitor.java | 57 + .../visitor/AllRequiresInfoVisitor.java | 57 + .../module/visitor/ExportsInfoVisitor.java | 37 + .../module/visitor/OpensInfoVisitor.java | 37 + .../module/visitor/ProvidesInfoVisitor.java | 37 + .../module/visitor/RequiresInfoVisitor.java | 37 + .../attribute/module/visitor/package.html | 3 + .../proguard/classfile/attribute/package.html | 0 .../attribute/preverification/DoubleType.java | 2 +- .../attribute/preverification/FloatType.java | 2 +- .../attribute/preverification/FullFrame.java | 2 +- .../preverification/IntegerType.java | 2 +- .../preverification/LessZeroFrame.java | 2 +- .../attribute/preverification/LongType.java | 2 +- .../preverification/MoreZeroFrame.java | 2 +- .../attribute/preverification/NullType.java | 2 +- .../attribute/preverification/ObjectType.java | 2 +- .../preverification/SameOneFrame.java | 2 +- .../preverification/SameZeroFrame.java | 2 +- .../preverification/StackMapAttribute.java | 4 +- .../preverification/StackMapFrame.java | 2 +- .../StackMapTableAttribute.java | 2 +- .../attribute/preverification/TopType.java | 2 +- .../UninitializedThisType.java | 2 +- .../preverification/UninitializedType.java | 2 +- .../preverification/VerificationType.java | 2 +- .../VerificationTypeFactory.java | 2 +- .../visitor/StackMapFrameVisitor.java | 2 +- .../visitor/VerificationTypeVisitor.java | 2 +- .../visitor/AllAttributeVisitor.java | 2 +- .../AllBootstrapMethodInfoVisitor.java | 2 +- .../visitor/AllExceptionInfoVisitor.java | 2 +- .../visitor/AllInnerClassesInfoVisitor.java | 2 +- .../visitor/AllLineNumberInfoVisitor.java | 2 +- .../attribute/visitor/AttributeCounter.java | 59 + .../visitor/AttributeNameFilter.java | 30 +- .../visitor/AttributeToClassVisitor.java | 55 + .../attribute/visitor/AttributeVisitor.java | 23 +- .../visitor/BootstrapMethodInfoVisitor.java | 2 +- .../visitor/DebugAttributeVisitor.java | 591 ++ .../visitor/ExceptionInfoVisitor.java | 2 +- .../visitor/InnerClassesInfoVisitor.java | 2 +- .../InstructionToAttributeVisitor.java | 58 + .../visitor/LineNumberInfoVisitor.java | 2 +- .../visitor/LineNumberRangeFinder.java | 2 +- .../visitor/LocalVariableInfoVisitor.java | 2 +- .../visitor/LocalVariableTypeInfoVisitor.java | 2 +- .../visitor/MultiAttributeVisitor.java | 127 +- .../visitor/NonEmptyAttributeFilter.java | 31 +- .../visitor/ParameterInfoVisitor.java | 2 +- .../visitor/RequiredAttributeFilter.java | 30 +- .../attribute/visitor/StackSizeComputer.java | 4 +- .../classfile/attribute/visitor/package.html | 0 .../classfile/constant/ClassConstant.java | 2 +- .../proguard/classfile/constant/Constant.java | 2 +- .../classfile/constant/DoubleConstant.java | 2 +- .../classfile/constant/FieldrefConstant.java | 2 +- .../classfile/constant/FloatConstant.java | 2 +- .../classfile/constant/IntegerConstant.java | 2 +- .../constant/InterfaceMethodrefConstant.java | 2 +- .../constant/InvokeDynamicConstant.java | 2 +- .../classfile/constant/LongConstant.java | 2 +- .../constant/MethodHandleConstant.java | 2 +- .../constant/MethodTypeConstant.java | 2 +- .../classfile/constant/MethodrefConstant.java | 2 +- .../classfile/constant/ModuleConstant.java | 74 + .../constant/NameAndTypeConstant.java | 2 +- .../classfile/constant/PackageConstant.java | 74 + .../constant/PrimitiveArrayConstant.java | 230 + .../classfile/constant/RefConstant.java | 2 +- .../classfile/constant/StringConstant.java | 2 +- .../classfile/constant/Utf8Constant.java | 2 +- .../constant/visitor/AllConstantVisitor.java | 2 +- .../BootstrapMethodArgumentVisitor.java | 2 +- .../BootstrapMethodHandleTraveler.java | 2 +- .../constant/visitor/ConstantCounter.java | 58 + .../constant/visitor/ConstantTagFilter.java | 2 +- .../constant/visitor/ConstantVisitor.java | 5 +- .../visitor/ExceptClassConstantFilter.java | 2 +- .../constant/visitor/MethodrefTraveler.java | 2 +- .../PrimitiveArrayConstantElementVisitor.java | 42 + .../PrimitiveArrayConstantVisitor.java | 42 + .../visitor/SuperClassConstantVisitor.java | 2 +- .../classfile/constant/visitor/package.html | 0 .../classfile/editor/AccessFixer.java | 4 +- .../classfile/editor/AnnotationAdder.java | 2 +- .../editor/AnnotationsAttributeEditor.java | 2 +- .../classfile/editor/AttributeAdder.java | 2 +- .../classfile/editor/AttributeSorter.java | 2 +- .../classfile/editor/AttributesEditor.java | 2 +- .../editor/BootstrapMethodInfoAdder.java | 2 +- .../editor/BootstrapMethodRemapper.java | 44 +- .../BootstrapMethodsAttributeAdder.java | 2 +- .../BootstrapMethodsAttributeEditor.java | 43 +- .../BootstrapMethodsAttributeShrinker.java | 339 ++ .../classfile/editor/BridgeMethodFixer.java | 2 +- .../classfile/editor/ClassEditor.java | 2 +- .../classfile/editor/ClassElementSorter.java | 2 +- .../classfile/editor/ClassMemberSorter.java | 2 +- .../classfile/editor/ClassReferenceFixer.java | 2 +- .../editor/CodeAttributeComposer.java | 2 +- .../classfile/editor/CodeAttributeEditor.java | 480 +- .../editor/CodeAttributeEditorResetter.java | 2 +- .../editor/CompactCodeAttributeComposer.java | 2075 +++++++ .../classfile/editor/ComparableConstant.java | 50 +- .../classfile/editor/ConstantAdder.java | 23 +- .../classfile/editor/ConstantPoolEditor.java | 160 +- .../editor/ConstantPoolRemapper.java | 107 +- .../editor/ConstantPoolShrinker.java | 124 +- .../classfile/editor/ConstantPoolSorter.java | 2 +- .../classfile/editor/ElementValueAdder.java | 2 +- .../classfile/editor/ElementValuesEditor.java | 2 +- .../classfile/editor/ExceptionAdder.java | 2 +- .../classfile/editor/ExceptionInfoAdder.java | 2 +- .../classfile/editor/ExceptionInfoEditor.java | 92 + .../editor/ExceptionsAttributeEditor.java | 2 +- .../editor/InnerClassesAccessFixer.java | 2 +- .../editor/InnerClassesAttributeEditor.java | 92 + .../classfile/editor/InstructionAdder.java | 2 +- .../editor/InstructionSequenceBuilder.java | 1981 +++++++ .../classfile/editor/InstructionWriter.java | 2 +- .../classfile/editor/InterfaceAdder.java | 2 +- .../classfile/editor/InterfaceDeleter.java | 2 +- .../classfile/editor/InterfaceSorter.java | 2 +- .../classfile/editor/InterfacesEditor.java | 2 +- .../classfile/editor/LineNumberInfoAdder.java | 2 +- .../LineNumberTableAttributeEditor.java | 2 +- .../LineNumberTableAttributeTrimmer.java | 2 +- .../editor/LocalVariableInfoAdder.java | 2 +- .../LocalVariableTableAttributeEditor.java | 2 +- .../editor/LocalVariableTypeInfoAdder.java | 2 +- ...LocalVariableTypeTableAttributeEditor.java | 2 +- .../classfile/editor/MemberAdder.java | 82 +- .../editor/MemberReferenceFixer.java | 2 +- .../classfile/editor/MemberRemover.java | 95 + .../editor/MethodInvocationFixer.java | 2 +- .../classfile/editor/NameAndTypeShrinker.java | 2 +- .../editor/NamedAttributeDeleter.java | 2 +- .../ParameterAnnotationsAttributeEditor.java | 2 +- .../classfile/editor/ParameterInfoAdder.java | 8 +- .../editor/SimplifiedClassEditor.java | 517 ++ .../classfile/editor/StackSizeUpdater.java | 2 +- .../classfile/editor/SubclassAdder.java | 2 +- .../classfile/editor/SubclassToAdder.java | 2 +- .../classfile/editor/Utf8Shrinker.java | 5 +- .../classfile/editor/VariableCleaner.java | 2 +- .../classfile/editor/VariableEditor.java | 2 +- .../classfile/editor/VariableRemapper.java | 2 +- .../classfile/editor/VariableSizeUpdater.java | 2 +- .../proguard/classfile/editor/package.html | 0 .../instruction/BranchInstruction.java | 6 +- .../instruction/ConstantInstruction.java | 5 +- .../classfile/instruction/Instruction.java | 2 +- .../instruction/InstructionConstants.java | 2 +- .../instruction/InstructionFactory.java | 2 +- .../instruction/InstructionUtil.java | 30 +- .../instruction/LookUpSwitchInstruction.java | 2 +- .../instruction/SimpleInstruction.java | 2 +- .../instruction/SwitchInstruction.java | 2 +- .../instruction/TableSwitchInstruction.java | 2 +- .../instruction/VariableInstruction.java | 2 +- .../classfile/instruction/package.html | 0 .../visitor/AllInstructionVisitor.java | 2 +- .../visitor/InstructionConstantVisitor.java | 2 +- .../visitor/InstructionCounter.java | 2 +- .../visitor/InstructionVisitor.java | 2 +- .../visitor/MultiInstructionVisitor.java | 35 +- .../instruction/visitor/package.html | 0 .../classfile/io/LibraryClassReader.java | 45 +- .../classfile/io/ProgramClassReader.java | 327 +- .../classfile/io/ProgramClassWriter.java | 214 +- .../classfile/io/RuntimeDataInput.java | 2 +- .../classfile/io/RuntimeDataOutput.java | 2 +- .../src}/proguard/classfile/io/package.html | 0 .../src}/proguard/classfile/package.html | 0 .../proguard/classfile/util/AccessUtil.java | 2 +- .../classfile/util/AllParameterVisitor.java | 35 +- .../util/ArrayInitializationMatcher.java | 347 ++ .../util/ArrayInitializationReplacer.java | 200 + .../util/ClassReferenceInitializer.java | 47 +- .../util/ClassSubHierarchyInitializer.java | 2 +- .../util/ClassSuperHierarchyInitializer.java | 12 +- .../proguard/classfile/util/ClassUtil.java | 494 +- .../util/DescriptorClassEnumeration.java | 2 +- .../DynamicClassReferenceInitializer.java | 2 +- .../DynamicMemberReferenceInitializer.java | 971 ++++ .../util/EnumFieldReferenceInitializer.java | 2 +- .../util/ExternalTypeEnumeration.java | 2 +- .../util/InstructionSequenceMatcher.java | 122 +- .../util/InternalTypeEnumeration.java | 2 +- .../proguard/classfile/util/MemberFinder.java | 106 +- .../proguard/classfile/util/MethodLinker.java | 2 +- .../util/PrimitiveArrayConstantReplacer.java | 218 + .../classfile/util/SimplifiedVisitor.java | 149 +- .../util/StringReferenceInitializer.java | 2 +- .../proguard/classfile/util/StringSharer.java | 2 +- .../classfile/util/WarningPrinter.java | 2 +- .../src}/proguard/classfile/util/package.html | 0 .../classfile/visitor/AllClassVisitor.java | 2 +- .../classfile/visitor/AllFieldVisitor.java | 2 +- .../classfile/visitor/AllMemberVisitor.java | 2 +- .../classfile/visitor/AllMethodVisitor.java | 2 +- .../classfile/visitor/BottomClassFilter.java | 2 +- .../classfile/visitor/ClassAccessFilter.java | 2 +- .../classfile/visitor/ClassCleaner.java | 2 +- .../classfile/visitor/ClassCollector.java | 2 +- .../classfile/visitor/ClassCounter.java | 2 +- .../visitor/ClassHierarchyTraveler.java | 2 +- .../classfile/visitor/ClassNameFilter.java | 58 +- .../visitor/ClassPoolClassVisitor.java | 70 + .../classfile/visitor/ClassPoolFiller.java | 2 +- .../classfile/visitor/ClassPoolRemover.java | 2 +- .../classfile/visitor/ClassPoolVisitor.java | 2 +- .../visitor/ClassPresenceFilter.java | 2 +- .../classfile/visitor/ClassPrinter.java | 256 +- .../classfile/visitor/ClassVersionFilter.java | 2 +- .../classfile/visitor/ClassVersionSetter.java | 2 +- .../classfile/visitor/ClassVisitor.java | 2 +- .../visitor/ConcreteClassDownTraveler.java | 2 +- .../visitor/ConstructorMethodFilter.java | 141 + .../visitor/DotClassClassVisitor.java | 2 +- .../visitor/DynamicReturnedClassVisitor.java | 2 +- .../classfile/visitor/ExceptClassFilter.java | 2 +- .../visitor/ExceptClassesFilter.java | 2 +- .../classfile/visitor/ExceptionCounter.java | 2 +- .../ExceptionExcludedOffsetFilter.java | 2 +- .../ExceptionHandlerConstantVisitor.java | 2 +- .../visitor/ExceptionHandlerFilter.java | 2 +- .../visitor/ExceptionOffsetFilter.java | 2 +- .../visitor/ExceptionRangeFilter.java | 2 +- .../visitor/FunctionalInterfaceFilter.java | 87 + .../ImplementedClassConstantFilter.java | 2 +- .../visitor/ImplementedClassFilter.java | 2 +- .../ImplementingClassConstantFilter.java | 2 +- .../visitor/InitializerMethodFilter.java | 2 +- .../classfile/visitor/LibraryClassFilter.java | 2 +- .../visitor/LibraryMemberFilter.java | 2 +- .../classfile/visitor/MemberAccessFilter.java | 6 +- .../visitor/MemberAccessFlagCleaner.java | 76 +- .../visitor/MemberAccessFlagSetter.java | 66 + .../visitor/MemberClassAccessFilter.java | 2 +- .../classfile/visitor/MemberCollector.java | 92 + .../classfile/visitor/MemberCounter.java | 2 +- .../visitor/MemberDescriptorFilter.java | 33 +- ...emberDescriptorReferencedClassVisitor.java | 2 +- .../classfile/visitor/MemberNameFilter.java | 33 +- .../visitor/MemberToClassVisitor.java | 35 +- .../classfile/visitor/MemberVisitor.java | 2 +- .../classfile/visitor/MethodCollector.java | 34 +- .../visitor/MethodImplementationFilter.java | 2 +- .../visitor/MethodImplementationTraveler.java | 2 +- .../visitor/MultiClassPoolVisitor.java | 2 +- .../classfile/visitor/MultiClassVisitor.java | 34 +- .../classfile/visitor/MultiMemberVisitor.java | 34 +- .../classfile/visitor/NamedClassVisitor.java | 2 +- .../classfile/visitor/NamedFieldVisitor.java | 2 +- .../classfile/visitor/NamedMethodVisitor.java | 2 +- .../visitor/ParallelAllClassVisitor.java | 202 + .../classfile/visitor/ParameterVisitor.java | 2 +- .../classfile/visitor/ProgramClassFilter.java | 2 +- .../visitor/ProgramMemberFilter.java | 2 +- .../visitor/ReferencedClassVisitor.java | 2 +- .../visitor/ReferencedMemberVisitor.java | 2 +- .../visitor/SimilarMemberVisitor.java | 2 +- .../classfile/visitor/SimpleClassPrinter.java | 44 +- .../visitor/SingleTimeClassVisitor.java | 33 +- .../classfile/visitor/SubclassFilter.java | 2 +- .../classfile/visitor/SubclassTraveler.java | 2 +- .../visitor/VariableClassVisitor.java | 2 +- .../visitor/VariableMemberVisitor.java | 2 +- .../proguard/classfile/visitor/package.html | 0 .../configuration/ConfigurationLogger.java | 789 +++ .../ConfigurationLoggingAdder.java | 129 + ...onLoggingInstructionSequenceConstants.java | 479 ++ ...ionLoggingInstructionSequenceReplacer.java | 130 + ...onLoggingInstructionSequencesReplacer.java | 146 + .../proguard/evaluation/BasicBranchUnit.java | 38 +- .../evaluation/BasicInvocationUnit.java | 230 + .../src}/proguard/evaluation/BranchUnit.java | 2 +- .../evaluation/ClassConstantValueFactory.java | 5 +- .../evaluation/ConstantValueFactory.java | 16 +- .../proguard/evaluation/InvocationUnit.java | 13 +- .../src}/proguard/evaluation/Processor.java | 84 +- .../evaluation/SimplifiedInvocationUnit.java | 314 + .../src}/proguard/evaluation/Stack.java | 2 +- .../src}/proguard/evaluation/TracedStack.java | 2 +- .../proguard/evaluation/TracedVariables.java | 2 +- .../src}/proguard/evaluation/Variables.java | 2 +- .../evaluation/value/ArrayReferenceValue.java | 21 +- .../value/ArrayReferenceValueFactory.java | 47 + .../evaluation/value/BasicValueFactory.java | 108 +- .../evaluation/value/Category1Value.java | 2 +- .../evaluation/value/Category2Value.java | 2 +- .../evaluation/value/ComparisonValue.java | 2 +- .../value/CompositeDoubleValue.java | 2 +- .../evaluation/value/CompositeFloatValue.java | 2 +- .../value/CompositeIntegerValue.java | 2 +- .../evaluation/value/CompositeLongValue.java | 4 +- .../evaluation/value/ConvertedByteValue.java | 2 +- .../value/ConvertedCharacterValue.java | 2 +- .../value/ConvertedDoubleValue.java | 2 +- .../evaluation/value/ConvertedFloatValue.java | 2 +- .../value/ConvertedIntegerValue.java | 2 +- .../evaluation/value/ConvertedLongValue.java | 2 +- .../evaluation/value/ConvertedShortValue.java | 2 +- .../value/DetailedArrayReferenceValue.java | 66 +- .../value/DetailedArrayValueFactory.java | 7 +- .../evaluation/value/DoubleValue.java | 2 +- .../proguard/evaluation/value/FloatValue.java | 2 +- .../value/IdentifiedArrayReferenceValue.java | 67 +- .../value/IdentifiedDoubleValue.java | 2 +- .../value/IdentifiedFloatValue.java | 2 +- .../value/IdentifiedIntegerValue.java | 2 +- .../evaluation/value/IdentifiedLongValue.java | 2 +- .../value/IdentifiedReferenceValue.java | 71 +- .../value/IdentifiedValueFactory.java | 17 +- .../evaluation/value/InitialValueFactory.java | 2 +- .../value/InstructionOffsetValue.java | 231 +- .../evaluation/value/IntegerValue.java | 2 +- .../proguard/evaluation/value/LongValue.java | 2 +- .../evaluation/value/NegatedDoubleValue.java | 2 +- .../evaluation/value/NegatedFloatValue.java | 2 +- .../evaluation/value/NegatedIntegerValue.java | 2 +- .../evaluation/value/NegatedLongValue.java | 2 +- .../value/ParticularDoubleValue.java | 4 +- .../value/ParticularFloatValue.java | 4 +- .../value/ParticularIntegerValue.java | 2 +- .../evaluation/value/ParticularLongValue.java | 2 +- .../value/ParticularValueFactory.java | 76 +- .../PrimitiveTypedReferenceValueFactory.java | 70 + .../evaluation/value/ReferenceValue.java | 93 +- .../evaluation/value/SpecificDoubleValue.java | 6 +- .../evaluation/value/SpecificFloatValue.java | 6 +- .../value/SpecificIntegerValue.java | 4 +- .../evaluation/value/SpecificLongValue.java | 4 +- .../proguard/evaluation/value/TopValue.java | 2 +- .../value/TracedReferenceValue.java | 335 ++ .../evaluation/value/TracingValue.java | 165 + .../evaluation/value/TypedReferenceValue.java | 386 +- .../value/TypedReferenceValueFactory.java | 84 + .../evaluation/value/UnknownDoubleValue.java | 10 +- .../evaluation/value/UnknownFloatValue.java | 10 +- .../evaluation/value/UnknownIntegerValue.java | 14 +- .../evaluation/value/UnknownLongValue.java | 10 +- .../value/UnknownReferenceValue.java | 206 + .../src}/proguard/evaluation/value/Value.java | 2 +- .../evaluation/value/ValueFactory.java | 132 + .../proguard/evaluation/value/package.html | 0 .../proguard/io/CascadingDataEntryWriter.java | 25 +- .../src/proguard/io/ClassDataEntryWriter.java | 135 + .../src}/proguard/io/ClassFilter.java | 2 +- .../proguard/io/ClassMapDataEntryWriter.java | 164 + core/src/proguard/io/ClassPathDataEntry.java | 118 + .../src}/proguard/io/ClassReader.java | 6 +- {src => core/src}/proguard/io/DataEntry.java | 16 +- .../proguard/io/DataEntryClassWriter.java | 16 +- .../src}/proguard/io/DataEntryCopier.java | 129 +- .../proguard/io/DataEntryDirectoryFilter.java | 2 +- .../src}/proguard/io/DataEntryFilter.java | 2 +- .../src}/proguard/io/DataEntryNameFilter.java | 2 +- .../proguard/io/DataEntryParentFilter.java | 2 +- .../src}/proguard/io/DataEntryPump.java | 2 +- .../src}/proguard/io/DataEntryReader.java | 2 +- .../src}/proguard/io/DataEntryRewriter.java | 13 +- .../src}/proguard/io/DataEntryWriter.java | 42 +- .../src}/proguard/io/DirectoryFilter.java | 2 +- .../src}/proguard/io/DirectoryPump.java | 6 +- .../src}/proguard/io/DirectoryWriter.java | 89 +- .../src/proguard/io/ExtraDataEntryWriter.java | 182 + .../src}/proguard/io/FileDataEntry.java | 41 +- .../proguard/io/FilteredDataEntryReader.java | 2 +- .../proguard/io/FilteredDataEntryWriter.java | 31 +- {src => core/src}/proguard/io/Finisher.java | 2 +- core/src/proguard/io/IdleRewriter.java | 51 + {src => core/src}/proguard/io/JarReader.java | 22 +- core/src/proguard/io/JarWriter.java | 219 + .../src}/proguard/io/ManifestRewriter.java | 6 +- {src => core/src}/proguard/io/NameFilter.java | 2 +- .../io/NameFilteredDataEntryWriter.java | 108 + .../proguard/io/ParentDataEntryWriter.java | 25 +- .../io/PrefixAddingDataEntryWriter.java | 95 + .../io/PrefixStrippingDataEntryReader.java | 65 + .../src}/proguard/io/RenamedDataEntry.java | 37 +- .../proguard/io/RenamedDataEntryReader.java | 34 +- .../proguard/io/RenamedDataEntryWriter.java | 55 +- .../io/RenamedParentDataEntryWriter.java | 116 + core/src/proguard/io/WrappedDataEntry.java | 87 + .../src}/proguard/io/ZipDataEntry.java | 14 +- core/src/proguard/io/ZipFileDataEntry.java | 123 + core/src/proguard/io/ZipOutput.java | 577 ++ {src => core/src}/proguard/io/package.html | 0 .../proguard/obfuscate/AttributeShrinker.java | 2 +- .../obfuscate/AttributeUsageMarker.java | 2 +- .../proguard/obfuscate/ClassObfuscator.java | 2 +- .../src}/proguard/obfuscate/ClassRenamer.java | 2 +- .../obfuscate/DictionaryNameFactory.java | 113 +- .../src}/proguard/obfuscate/MapCleaner.java | 2 +- .../proguard/obfuscate/MappingKeeper.java | 2 +- .../proguard/obfuscate/MappingPrinter.java | 2 +- .../proguard/obfuscate/MappingProcessor.java | 2 +- .../proguard/obfuscate/MappingReader.java | 2 +- .../proguard/obfuscate/MemberNameCleaner.java | 2 +- .../obfuscate/MemberNameCollector.java | 2 +- .../obfuscate/MemberNameConflictFixer.java | 2 +- .../proguard/obfuscate/MemberNameFilter.java | 2 +- .../proguard/obfuscate/MemberObfuscator.java | 2 +- .../obfuscate/MemberSpecialNameFilter.java | 2 +- .../obfuscate/MultiMappingProcessor.java | 2 +- .../src}/proguard/obfuscate/NameFactory.java | 2 +- .../obfuscate/NameFactoryResetter.java | 2 +- .../src}/proguard/obfuscate/NameMarker.java | 2 +- .../obfuscate/NumericNameFactory.java | 2 +- .../src}/proguard/obfuscate/Obfuscator.java | 74 +- .../obfuscate/ParameterNameMarker.java | 2 +- .../obfuscate/PrefixingNameFactory.java | 60 + .../proguard/obfuscate/RenamedFlagSetter.java | 73 + .../proguard/obfuscate/SimpleNameFactory.java | 2 +- .../proguard/obfuscate/SourceFileRenamer.java | 2 +- .../obfuscate/SpecialNameFactory.java | 2 +- .../obfuscate/UniqueMemberNameFactory.java | 93 + .../src}/proguard/obfuscate/package.html | 0 .../BootstrapMethodArgumentShrinker.java | 2 +- .../proguard/optimize/ChangedCodePrinter.java | 21 +- .../optimize/ConstantMemberFilter.java | 2 +- .../optimize/ConstantParameterFilter.java | 2 +- .../optimize/DuplicateInitializerFixer.java | 30 +- .../DuplicateInitializerInvocationFixer.java | 2 +- core/src/proguard/optimize/KeepMarker.java | 134 + .../proguard/optimize/KeptClassFilter.java | 94 + .../proguard/optimize/KeptMemberFilter.java | 2 +- .../optimize/MemberDescriptorSpecializer.java | 2 +- .../optimize/MethodDescriptorShrinker.java | 15 +- .../proguard/optimize/MethodStaticizer.java | 6 +- .../optimize/OptimizationInfoClassFilter.java | 22 +- .../OptimizationInfoMemberFilter.java | 65 +- core/src/proguard/optimize/Optimizer.java | 1691 ++++++ .../proguard/optimize/ParameterShrinker.java | 20 +- .../optimize/TailRecursionSimplifier.java | 56 +- .../optimize/WriteOnlyFieldFilter.java | 2 +- .../evaluation/EvaluationShrinker.java | 1548 +++++ .../evaluation/EvaluationSimplifier.java | 298 +- .../evaluation/InitializationFinder.java | 349 ++ .../evaluation/InstructionUsageMarker.java | 1731 ++++++ .../optimize/evaluation/LivenessAnalyzer.java | 200 +- .../evaluation/LoadingInvocationUnit.java | 30 +- .../ParameterTracingInvocationUnit.java | 172 + .../optimize/evaluation/PartialEvaluator.java | 448 +- .../ReferenceTracingInvocationUnit.java | 211 + .../ReferenceTracingValueFactory.java | 301 + .../evaluation/SimpleEnumArrayPropagator.java | 68 +- .../evaluation/SimpleEnumClassChecker.java | 18 +- .../evaluation/SimpleEnumClassSimplifier.java | 57 +- .../SimpleEnumDescriptorSimplifier.java | 10 +- .../evaluation/SimpleEnumUseChecker.java | 27 +- .../evaluation/SimpleEnumUseSimplifier.java | 32 +- .../evaluation/StoringInvocationUnit.java | 69 +- .../optimize/evaluation/TracedBranchUnit.java | 39 +- .../evaluation/VariableOptimizer.java | 4 +- .../proguard/optimize/evaluation/package.html | 0 .../optimize/info/AccessMethodMarker.java | 31 +- .../optimize/info/BackwardBranchMarker.java | 11 +- .../optimize/info/CatchExceptionMarker.java | 12 +- .../optimize/info/CaughtClassFilter.java | 2 +- .../optimize/info/CaughtClassMarker.java | 11 +- .../optimize/info/ClassOptimizationInfo.java | 144 + .../info/CodeAttributeOptimizationInfo.java | 51 + .../optimize/info/DotClassFilter.java | 2 +- .../optimize/info/DotClassMarker.java | 23 +- .../info/DynamicInvocationMarker.java | 12 +- .../optimize/info/EscapingClassFilter.java | 99 + .../optimize/info/EscapingClassMarker.java | 222 + .../info/ExceptionInstructionChecker.java | 100 +- .../optimize/info/FieldOptimizationInfo.java | 82 + .../optimize/info/InstanceofClassFilter.java | 2 +- .../optimize/info/InstanceofClassMarker.java | 23 +- .../info/InstantiationClassFilter.java | 2 +- .../info/InstantiationClassMarker.java | 23 +- .../optimize/info/MethodInvocationMarker.java | 20 +- .../optimize/info/MethodOptimizationInfo.java | 291 + .../optimize/info/MutableBoolean.java | 66 + .../NoEscapingParametersMethodMarker.java | 72 + .../NoExternalReturnValuesMethodMarker.java | 73 + .../NoExternalSideEffectMethodMarker.java | 72 + .../info/NoSideEffectClassMarker.java | 35 +- .../info/NoSideEffectMethodMarker.java | 28 +- .../info/NonEmptyStackReturnMarker.java | 11 +- .../optimize/info/NonPrivateMemberMarker.java | 56 +- .../info/OptimizationCodeAttributeFilter.java | 88 + ...ageVisibleMemberContainingClassMarker.java | 13 +- ...ckageVisibleMemberInvokingClassMarker.java | 13 +- .../optimize/info/ParameterEscapeMarker.java | 924 +++ .../optimize/info/ParameterEscapedMarker.java | 307 + .../optimize/info/ParameterUsageMarker.java | 97 +- .../info/ProgramClassOptimizationInfo.java | 103 +- .../ProgramClassOptimizationInfoSetter.java | 70 + .../info/ProgramFieldOptimizationInfo.java | 63 +- .../ProgramMemberOptimizationInfoSetter.java | 83 + .../info/ProgramMethodOptimizationInfo.java | 588 ++ .../optimize/info/ReadWriteFieldMarker.java | 57 +- .../optimize/info/ReferenceEscapeChecker.java | 466 ++ .../info/RepeatedClassPoolVisitor.java | 87 + .../optimize/info/SideEffectClassChecker.java | 84 + .../optimize/info/SideEffectClassFilter.java | 12 +- .../optimize/info/SideEffectClassMarker.java | 33 +- .../info/SideEffectInstructionChecker.java | 108 +- .../optimize/info/SideEffectMethodFilter.java | 2 +- .../optimize/info/SideEffectMethodMarker.java | 120 + .../optimize/info/SimpleEnumFilter.java | 2 +- .../optimize/info/SimpleEnumMarker.java | 11 +- .../optimize/info/SuperInvocationMarker.java | 11 +- .../info/SynchronizedBlockMethodMarker.java | 69 + .../info/UnusedParameterMethodFilter.java | 44 +- ...nusedParameterOptimizationInfoUpdater.java | 140 + .../optimize/info/UsedParameterFilter.java | 85 + .../optimize/info/VariableUsageMarker.java | 5 +- .../optimize/info/WrapperClassMarker.java | 225 + .../src}/proguard/optimize/info/package.html | 0 .../src}/proguard/optimize/package.html | 0 .../peephole}/BranchTargetFinder.java | 194 +- .../optimize/peephole/ClassFinalizer.java | 2 +- .../optimize/peephole/ClassMerger.java | 183 +- .../peephole/GotoCommonCodeReplacer.java | 7 +- .../optimize/peephole/GotoGotoReplacer.java | 2 +- .../optimize/peephole/GotoReturnReplacer.java | 2 +- .../peephole/HorizontalClassMerger.java | 13 +- .../InstructionSequenceConstants.java | 4772 +++++++++++++++ .../peephole/InstructionSequenceReplacer.java | 875 +++ .../InstructionSequencesReplacer.java | 52 +- .../peephole/LineNumberLinearizer.java | 2 +- .../optimize/peephole/MemberPrivatizer.java | 2 +- .../optimize/peephole/MethodFinalizer.java | 2 +- .../optimize/peephole/MethodInliner.java | 125 +- .../optimize/peephole/NopRemover.java | 2 +- .../optimize/peephole/PeepholeOptimizer.java | 5 +- .../peephole/ReachableCodeMarker.java | 2 +- .../peephole/RetargetedClassFilter.java | 2 +- .../RetargetedInnerClassAttributeRemover.java | 2 +- .../optimize/peephole/TargetClassChanger.java | 2 +- .../peephole/UnreachableCodeRemover.java | 2 +- .../peephole/UnreachableExceptionRemover.java | 2 +- .../optimize/peephole/VariableShrinker.java | 2 +- .../peephole/VerticalClassMerger.java | 15 +- .../peephole/WildcardConstantFilter.java | 300 + .../optimize/peephole/WrapperClassMerger.java | 99 + .../peephole/WrapperClassUseSimplifier.java | 275 + .../proguard/optimize/peephole/package.html | 0 {src => core/src}/proguard/package.html | 0 .../proguard/preverify/CodePreverifier.java | 87 +- .../preverify/CodeSubroutineInliner.java | 4 +- .../src}/proguard/preverify/Preverifier.java | 2 +- .../proguard/preverify/SubroutineInliner.java | 5 +- .../shrink/AnnotationUsageMarker.java | 2 +- .../src}/proguard/shrink/ClassShrinker.java | 22 +- .../proguard/shrink/InnerUsageMarker.java | 2 +- .../proguard/shrink/InterfaceUsageMarker.java | 2 +- .../shrink/LocalVariableTypeUsageMarker.java | 2 +- .../proguard/shrink/ShortestUsageMark.java | 2 +- .../proguard/shrink/ShortestUsageMarker.java | 2 +- .../proguard/shrink/ShortestUsagePrinter.java | 2 +- .../src}/proguard/shrink/Shrinker.java | 27 +- .../src}/proguard/shrink/UsageMarker.java | 133 +- .../src}/proguard/shrink/UsagePrinter.java | 2 +- .../src}/proguard/shrink/UsedClassFilter.java | 2 +- .../proguard/shrink/UsedMemberFilter.java | 2 +- .../src}/proguard/shrink/package.html | 0 .../src}/proguard/util/AndMatcher.java | 12 +- .../src}/proguard/util/ArrayUtil.java | 1094 +++- core/src/proguard/util/ClassNameParser.java | 364 ++ core/src/proguard/util/CollectionMatcher.java | 55 + .../src}/proguard/util/ConstantMatcher.java | 5 +- core/src/proguard/util/Counter.java | 34 + .../proguard/util/EmptyStringMatcher.java | 7 +- .../src}/proguard/util/ExtensionMatcher.java | 15 +- .../src}/proguard/util/FileNameParser.java | 2 +- .../proguard/util/FixedStringMatcher.java | 28 +- .../src}/proguard/util/ListMatcher.java | 9 +- .../src}/proguard/util/ListParser.java | 2 +- {src => core/src}/proguard/util/ListUtil.java | 2 +- .../proguard/util/MatchedStringMatcher.java | 72 + core/src/proguard/util/MultiValueMap.java | 92 + core/src/proguard/util/NameParser.java | 224 + .../src}/proguard/util/NotMatcher.java | 7 +- .../src}/proguard/util/ObjectUtil.java | 2 +- .../src}/proguard/util/OrMatcher.java | 12 +- core/src/proguard/util/PrintWriterUtil.java | 136 + .../src}/proguard/util/SettableMatcher.java | 7 +- .../src}/proguard/util/StringMatcher.java | 14 +- .../src}/proguard/util/StringParser.java | 2 +- core/src/proguard/util/StringTransformer.java | 49 + core/src/proguard/util/StringUtil.java | 203 + .../proguard/util/VariableStringMatcher.java | 268 + {src => core/src}/proguard/util/package.html | 0 docs/FAQ.html | 11 +- docs/GPL_exception.html | 2 +- docs/acknowledgements.html | 4 +- docs/alternatives.html | 264 +- docs/downloads.html | 32 +- docs/feedback.html | 12 +- docs/index.html | 2 +- docs/license.html | 2 +- docs/main.html | 2 +- docs/manual/ant.html | 34 +- docs/manual/attributes.html | 2 +- docs/manual/examples.html | 159 +- docs/manual/gradle.html | 32 +- docs/manual/gui.html | 2 +- docs/manual/index.html | 2 +- docs/manual/introduction.html | 11 +- docs/manual/limitations.html | 2 +- docs/manual/optimizations.html | 2 +- docs/manual/refcard.html | 53 +- docs/manual/retrace/examples.html | 2 +- docs/manual/retrace/index.html | 2 +- docs/manual/retrace/introduction.html | 2 +- docs/manual/retrace/usage.html | 2 +- docs/manual/troubleshooting.html | 32 +- docs/manual/usage.html | 125 +- docs/manual/wtk.html | 2 +- docs/proguard.appdata.xml | 2 +- docs/quality.html | 4 +- docs/results.html | 2 +- docs/screenshots.html | 2 +- docs/testimonials.html | 4 +- docs/title.html | 2 +- examples/android/AndroidManifest.xml | 19 + examples/android/build.gradle | 63 + examples/android/debug.keystore | Bin 0 -> 1268 bytes examples/android/local.properties | 2 + examples/android/proguard-project.txt | 127 + examples/android/res/drawable/ic_launcher.png | Bin 0 -> 3986 bytes examples/android/res/values/colors.xml | 7 + examples/android/res/values/strings.xml | 4 + examples/android/res/values/styles.xml | 8 + .../src/com/example/HelloWorldActivity.java | 29 + examples/annotations/examples.pro | 7 +- examples/annotations/lib/annotations.jar | Bin 6123 -> 0 bytes examples/annotations/lib/annotations.pro | 118 - examples/ant/proguard.xml | 4 + examples/gradle/android.gradle | 131 +- examples/gradle/applets.gradle | 22 +- examples/gradle/applications.gradle | 23 +- examples/gradle/library.gradle | 25 +- examples/gradle/midlets.gradle | 10 +- examples/gradle/proguard.gradle | 24 +- examples/gradle/proguardall.gradle | 28 +- examples/gradle/proguardgui.gradle | 23 +- examples/gradle/retrace.gradle | 19 +- examples/gradle/scala.gradle | 23 +- examples/gradle/servlets.gradle | 22 +- examples/{ => standalone}/android.pro | 115 +- examples/{ => standalone}/applets.pro | 13 +- examples/{ => standalone}/applications.pro | 14 +- examples/{ => standalone}/library.pro | 13 +- examples/{ => standalone}/midlets.pro | 6 +- examples/{ => standalone}/proguard.pro | 20 +- examples/{ => standalone}/proguardall.pro | 24 +- examples/{ => standalone}/proguardgui.pro | 26 +- examples/{ => standalone}/retrace.pro | 16 +- examples/{ => standalone}/scala.pro | 14 +- examples/{ => standalone}/servlets.pro | 13 +- gradle/ant.properties | 3 + gradle/build.gradle | 21 + gradle/build.sh | 38 + gradle/build.xml | 69 + gradle/makefile | 26 + {buildscripts/maven/gradle => gradle}/pom.xml | 20 +- gradle/settings.gradle | 1 + .../src}/proguard/gradle/ProGuardTask.java | 158 +- gui/build.gradle | 25 + gui/build.sh | 25 + gui/build.xml | 61 + gui/makefile | 8 + {buildscripts/maven/gui => gui}/pom.xml | 33 +- gui/settings.gradle | 2 + .../gui => gui/src/META-INF}/MANIFEST.MF | 0 .../src}/proguard/gui/ClassPathPanel.java | 7 +- .../gui/ClassSpecificationDialog.java | 45 +- .../gui/ClassSpecificationsPanel.java | 3 +- .../proguard/gui/ExtensionFileFilter.java | 2 +- .../src}/proguard/gui/FilterBuilder.java | 2 +- .../src}/proguard/gui/FilterDialog.java | 102 +- .../src}/proguard/gui/GUIResources.java | 2 +- .../src}/proguard/gui/GUIResources.properties | 70 +- .../proguard/gui/KeepSpecificationsPanel.java | 7 +- {src => gui/src}/proguard/gui/ListPanel.java | 2 +- .../gui/MemberSpecificationDialog.java | 2 +- .../gui/MemberSpecificationsPanel.java | 2 +- .../proguard/gui/MessageDialogRunnable.java | 2 +- .../proguard/gui/OptimizationsDialog.java | 2 +- .../src}/proguard/gui/ProGuardGUI.java | 684 ++- .../src}/proguard/gui/ProGuardRunnable.java | 20 +- .../src}/proguard/gui/ReTraceRunnable.java | 2 +- {src => gui/src}/proguard/gui/SwingUtil.java | 2 +- {src => gui/src}/proguard/gui/TabbedPane.java | 2 +- .../proguard/gui/TextAreaOutputStream.java | 2 +- .../src}/proguard/gui/TextAreaWriter.java | 2 +- {src => gui/src}/proguard/gui/arrow.gif | Bin {src => gui/src}/proguard/gui/boilerplate.pro | 445 +- {src => gui/src}/proguard/gui/default.pro | 0 {src => gui/src}/proguard/gui/package.html | 0 .../proguard/gui/splash/BufferedSprite.java | 2 +- .../proguard/gui/splash/CircleSprite.java | 2 +- .../src}/proguard/gui/splash/ClipSprite.java | 2 +- .../src}/proguard/gui/splash/ColorSprite.java | 2 +- .../proguard/gui/splash/CompositeSprite.java | 2 +- .../proguard/gui/splash/ConstantColor.java | 2 +- .../proguard/gui/splash/ConstantDouble.java | 2 +- .../proguard/gui/splash/ConstantFont.java | 2 +- .../src}/proguard/gui/splash/ConstantInt.java | 2 +- .../proguard/gui/splash/ConstantString.java | 2 +- .../proguard/gui/splash/ConstantTiming.java | 2 +- .../src}/proguard/gui/splash/FontSprite.java | 2 +- .../src}/proguard/gui/splash/ImageSprite.java | 2 +- .../src}/proguard/gui/splash/LinearColor.java | 2 +- .../proguard/gui/splash/LinearDouble.java | 2 +- .../src}/proguard/gui/splash/LinearInt.java | 2 +- .../proguard/gui/splash/LinearTiming.java | 2 +- .../gui/splash/OverrideGraphics2D.java | 2 +- .../proguard/gui/splash/RectangleSprite.java | 2 +- .../proguard/gui/splash/SawToothTiming.java | 2 +- .../proguard/gui/splash/ShadowedSprite.java | 2 +- .../src}/proguard/gui/splash/SineTiming.java | 2 +- .../proguard/gui/splash/SmoothTiming.java | 2 +- .../src}/proguard/gui/splash/SplashPanel.java | 2 +- .../src}/proguard/gui/splash/Sprite.java | 2 +- .../src}/proguard/gui/splash/TextSprite.java | 2 +- .../proguard/gui/splash/TimeSwitchSprite.java | 2 +- .../src}/proguard/gui/splash/Timing.java | 2 +- .../proguard/gui/splash/TypeWriterString.java | 2 +- .../proguard/gui/splash/VariableColor.java | 2 +- .../proguard/gui/splash/VariableDouble.java | 2 +- .../proguard/gui/splash/VariableFont.java | 2 +- .../src}/proguard/gui/splash/VariableInt.java | 2 +- .../proguard/gui/splash/VariableSizeFont.java | 2 +- .../proguard/gui/splash/VariableString.java | 2 +- .../src}/proguard/gui/splash/package.html | 0 {src => gui/src}/proguard/gui/vtitle.png | Bin lib/annotations.jar | Bin 0 -> 6341 bytes lib/proguard.jar | Bin 0 -> 1092740 bytes lib/proguardgui.jar | Bin 0 -> 145012 bytes lib/retrace.jar | Bin 0 -> 11308 bytes retrace/build.gradle | 24 + retrace/build.sh | 20 + retrace/build.xml | 55 + retrace/makefile | 8 + .../maven/retrace => retrace}/pom.xml | 26 +- retrace/settings.gradle | 1 + .../src/META-INF}/MANIFEST.MF | 0 .../src}/proguard/retrace/FrameInfo.java | 2 +- .../src}/proguard/retrace/FramePattern.java | 2 +- .../src}/proguard/retrace/FrameRemapper.java | 2 +- .../src}/proguard/retrace/ReTrace.java | 2 +- .../src}/proguard/retrace/package.html | 0 .../ClassSpecificationVisitorFactory.java | 529 -- src/proguard/DataEntryWriterFactory.java | 162 - src/proguard/OutputWriter.java | 219 - .../DynamicMemberReferenceInitializer.java | 942 --- .../evaluation/BasicInvocationUnit.java | 426 -- src/proguard/io/ClassRewriter.java | 80 - src/proguard/io/JarWriter.java | 234 - src/proguard/optimize/Optimizer.java | 1126 ---- .../evaluation/EvaluationShrinker.java | 2370 -------- .../optimize/info/MethodOptimizationInfo.java | 336 -- .../optimize/info/SideEffectMethodMarker.java | 181 - .../InstructionSequenceConstants.java | 5128 ----------------- .../peephole/InstructionSequenceReplacer.java | 422 -- src/proguard/util/ClassNameParser.java | 215 - src/proguard/util/NameParser.java | 106 - src/proguard/util/VariableStringMatcher.java | 126 - wtk/ant.properties | 3 + wtk/build.gradle | 21 + wtk/build.sh | 31 + wtk/build.xml | 66 + wtk/gradle.properties | 3 + wtk/makefile | 15 + {buildscripts/maven/wtk => wtk}/pom.xml | 25 +- wtk/settings.gradle | 1 + .../src}/proguard/wtk/ProGuardObfuscator.java | 2 +- {src => wtk/src}/proguard/wtk/default.pro | 0 {src => wtk/src}/proguard/wtk/package.html | 0 973 files changed, 51505 insertions(+), 17968 deletions(-) create mode 100644 annotations/build.gradle create mode 100755 annotations/build.sh create mode 100644 annotations/build.xml create mode 100644 annotations/makefile create mode 100644 annotations/pom.xml rename {examples/annotations => annotations}/src/proguard/annotation/Keep.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepApplication.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepClassMemberNames.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepClassMembers.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepGettersSetters.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepImplementations.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepName.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepPublicClassMemberNames.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepPublicClassMembers.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepPublicGettersSetters.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepPublicImplementations.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java (100%) rename {examples/annotations => annotations}/src/proguard/annotation/KeepPublicProtectedClassMembers.java (100%) create mode 100644 ant/build.gradle create mode 100755 ant/build.sh create mode 100644 ant/build.xml create mode 100644 ant/makefile rename {buildscripts/maven/ant => ant}/pom.xml (70%) create mode 100644 ant/settings.gradle rename {src => ant/src}/proguard/ant/ClassPathElement.java (99%) rename {src => ant/src}/proguard/ant/ClassSpecificationElement.java (96%) rename {src => ant/src}/proguard/ant/ConfigurationElement.java (98%) rename {src => ant/src}/proguard/ant/ConfigurationTask.java (76%) rename {src => ant/src}/proguard/ant/FilterElement.java (97%) rename {src => ant/src}/proguard/ant/KeepSpecificationElement.java (91%) rename {src => ant/src}/proguard/ant/MemberSpecificationElement.java (99%) rename {src => ant/src}/proguard/ant/ProGuardTask.java (87%) rename {src => ant/src}/proguard/ant/package.html (100%) rename {src => ant/src}/proguard/ant/task.properties (100%) delete mode 100644 buildscripts/build.properties create mode 100755 buildscripts/functions.sh create mode 100644 buildscripts/gradle.properties delete mode 100644 buildscripts/maven/base/pom.xml rename buildscripts/{maven => }/pom.xml (92%) create mode 100644 buildscripts/settings.gradle create mode 100644 core/build.gradle create mode 100755 core/build.sh create mode 100644 core/build.xml create mode 100644 core/makefile create mode 100644 core/pom.xml rename {src/proguard => core/src/META-INF}/MANIFEST.MF (100%) rename {src => core/src}/proguard/ArgumentWordReader.java (95%) rename {src => core/src}/proguard/AssumeNoSideEffectsChecker.java (98%) rename {src => core/src}/proguard/ClassMemberChecker.java (99%) rename {src => core/src}/proguard/ClassPath.java (97%) rename {src => core/src}/proguard/ClassPathEntry.java (80%) rename {src => core/src}/proguard/ClassSpecification.java (99%) create mode 100644 core/src/proguard/ClassSpecificationVisitorFactory.java rename {src => core/src}/proguard/Configuration.java (87%) rename {src => core/src}/proguard/ConfigurationChecker.java (96%) rename {src => core/src}/proguard/ConfigurationConstants.java (89%) rename {src => core/src}/proguard/ConfigurationParser.java (70%) rename {src => core/src}/proguard/ConfigurationWriter.java (85%) rename {src => core/src}/proguard/DataEntryReaderFactory.java (51%) create mode 100644 core/src/proguard/DataEntryWriterFactory.java rename {src => core/src}/proguard/DescriptorKeepChecker.java (91%) rename {src => core/src}/proguard/DuplicateClassPrinter.java (97%) rename {src => core/src}/proguard/FileWordReader.java (96%) rename {src => core/src}/proguard/FullyQualifiedClassNameChecker.java (97%) rename {src => core/src}/proguard/GPL.java (99%) rename {src => core/src}/proguard/GetAnnotationChecker.java (98%) rename {src => core/src}/proguard/GetEnclosingClassChecker.java (97%) rename {src => core/src}/proguard/GetEnclosingMethodChecker.java (97%) rename {src => core/src}/proguard/GetSignatureChecker.java (97%) rename {src => core/src}/proguard/Initializer.java (91%) rename {src => core/src}/proguard/InputReader.java (83%) rename {src => core/src}/proguard/KeepClassMemberChecker.java (98%) rename {src => core/src}/proguard/KeepClassSpecification.java (63%) create mode 100644 core/src/proguard/KeepClassSpecificationVisitorFactory.java rename {src => core/src}/proguard/LibraryKeepChecker.java (79%) rename {src => core/src}/proguard/LineWordReader.java (97%) rename {src => core/src}/proguard/MemberSpecification.java (95%) create mode 100644 core/src/proguard/OutputWriter.java rename {src => core/src}/proguard/ParseException.java (96%) rename {src => core/src}/proguard/ProGuard.java (71%) rename {src => core/src}/proguard/SeedPrinter.java (75%) rename {src => core/src}/proguard/SubclassedClassFilter.java (96%) rename {src => core/src}/proguard/Targeter.java (98%) rename {src => core/src}/proguard/UpToDateChecker.java (92%) rename {src => core/src}/proguard/WordReader.java (85%) create mode 100644 core/src/proguard/backport/Backporter.java create mode 100644 core/src/proguard/backport/DefaultInterfaceMethodConverter.java create mode 100644 core/src/proguard/backport/LambdaExpression.java create mode 100644 core/src/proguard/backport/LambdaExpressionCollector.java create mode 100644 core/src/proguard/backport/LambdaExpressionConverter.java create mode 100644 core/src/proguard/backport/StaticInterfaceMethodConverter.java create mode 100644 core/src/proguard/backport/StringConcatenationConverter.java rename {src => core/src}/proguard/classfile/ClassConstants.java (58%) rename {src => core/src}/proguard/classfile/ClassPool.java (98%) rename {src => core/src}/proguard/classfile/Clazz.java (99%) rename {src => core/src}/proguard/classfile/Field.java (94%) rename {src => core/src}/proguard/classfile/JavaConstants.java (91%) rename {src => core/src}/proguard/classfile/LibraryClass.java (99%) rename {src => core/src}/proguard/classfile/LibraryField.java (97%) rename {src => core/src}/proguard/classfile/LibraryMember.java (97%) rename {src => core/src}/proguard/classfile/LibraryMethod.java (97%) rename {src => core/src}/proguard/classfile/Member.java (96%) rename {src => core/src}/proguard/classfile/Method.java (94%) rename {src => core/src}/proguard/classfile/ProgramClass.java (84%) rename {src => core/src}/proguard/classfile/ProgramField.java (82%) rename {src => core/src}/proguard/classfile/ProgramMember.java (98%) rename {src => core/src}/proguard/classfile/ProgramMethod.java (84%) rename {src => core/src}/proguard/classfile/VisitorAccepter.java (96%) rename {src => core/src}/proguard/classfile/attribute/Attribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/BootstrapMethodInfo.java (97%) mode change 100755 => 100644 rename {src => core/src}/proguard/classfile/attribute/BootstrapMethodsAttribute.java (98%) mode change 100755 => 100644 rename {src => core/src}/proguard/classfile/attribute/CodeAttribute.java (84%) rename {src => core/src}/proguard/classfile/attribute/ConstantValueAttribute.java (96%) rename {src => core/src}/proguard/classfile/attribute/DeprecatedAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/EnclosingMethodAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/ExceptionInfo.java (94%) rename {src => core/src}/proguard/classfile/attribute/ExceptionsAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/ExtendedLineNumberInfo.java (96%) rename {src => core/src}/proguard/classfile/attribute/InnerClassesAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/InnerClassesInfo.java (85%) rename {src => core/src}/proguard/classfile/attribute/LineNumberInfo.java (96%) rename {src => core/src}/proguard/classfile/attribute/LineNumberTableAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/LocalVariableInfo.java (98%) rename {src => core/src}/proguard/classfile/attribute/LocalVariableTableAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/LocalVariableTypeInfo.java (98%) rename {src => core/src}/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/MethodParametersAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/ParameterInfo.java (81%) rename {src => core/src}/proguard/classfile/attribute/SignatureAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/SourceDirAttribute.java (96%) rename {src => core/src}/proguard/classfile/attribute/SourceFileAttribute.java (96%) rename {src => core/src}/proguard/classfile/attribute/SyntheticAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/UnknownAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/Annotation.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/AnnotationElementValue.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/AnnotationsAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/ArrayElementValue.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/ClassElementValue.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/ConstantElementValue.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/ElementValue.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/EnumConstantElementValue.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/RuntimeInvisibleTypeAnnotationsAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/RuntimeVisibleTypeAnnotationsAttribute.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/TypeAnnotation.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/TypeAnnotationsAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/TypePathInfo.java (96%) rename {src => core/src}/proguard/classfile/attribute/annotation/package.html (100%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/CatchTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/EmptyTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/FormalParameterTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/LocalVariableTargetElement.java (96%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/LocalVariableTargetInfo.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/OffsetTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/SuperTypeTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/TargetInfo.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/ThrowsTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/TypeArgumentTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/TypeParameterBoundTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/TypeParameterTargetInfo.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/visitor/LocalVariableTargetElementVisitor.java (96%) rename {src => core/src}/proguard/classfile/attribute/annotation/target/visitor/TargetInfoVisitor.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java (99%) rename {src => core/src}/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java (99%) rename src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java => core/src/proguard/classfile/attribute/annotation/visitor/AnnotationToAnnotatedClassVisitor.java (91%) rename src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java => core/src/proguard/classfile/attribute/annotation/visitor/AnnotationToAnnotatedMemberVisitor.java (91%) rename {src => core/src}/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java (62%) rename {src => core/src}/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java (98%) rename {src => core/src}/proguard/classfile/attribute/annotation/visitor/TypeAnnotationVisitor.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/visitor/TypePathInfoVisitor.java (97%) rename {src => core/src}/proguard/classfile/attribute/annotation/visitor/package.html (100%) create mode 100644 core/src/proguard/classfile/attribute/module/ExportsInfo.java create mode 100644 core/src/proguard/classfile/attribute/module/ModuleAttribute.java create mode 100644 core/src/proguard/classfile/attribute/module/ModuleMainClassAttribute.java create mode 100644 core/src/proguard/classfile/attribute/module/ModulePackagesAttribute.java create mode 100644 core/src/proguard/classfile/attribute/module/OpensInfo.java create mode 100644 core/src/proguard/classfile/attribute/module/ProvidesInfo.java create mode 100644 core/src/proguard/classfile/attribute/module/RequiresInfo.java create mode 100644 core/src/proguard/classfile/attribute/module/package.html create mode 100644 core/src/proguard/classfile/attribute/module/visitor/AllExportsInfoVisitor.java create mode 100644 core/src/proguard/classfile/attribute/module/visitor/AllOpensInfoVisitor.java create mode 100644 core/src/proguard/classfile/attribute/module/visitor/AllProvidesInfoVisitor.java create mode 100644 core/src/proguard/classfile/attribute/module/visitor/AllRequiresInfoVisitor.java create mode 100644 core/src/proguard/classfile/attribute/module/visitor/ExportsInfoVisitor.java create mode 100644 core/src/proguard/classfile/attribute/module/visitor/OpensInfoVisitor.java create mode 100644 core/src/proguard/classfile/attribute/module/visitor/ProvidesInfoVisitor.java create mode 100644 core/src/proguard/classfile/attribute/module/visitor/RequiresInfoVisitor.java create mode 100644 core/src/proguard/classfile/attribute/module/visitor/package.html rename {src => core/src}/proguard/classfile/attribute/package.html (100%) rename {src => core/src}/proguard/classfile/attribute/preverification/DoubleType.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/FloatType.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/FullFrame.java (99%) rename {src => core/src}/proguard/classfile/attribute/preverification/IntegerType.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/LessZeroFrame.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/LongType.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/MoreZeroFrame.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/NullType.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/ObjectType.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/SameOneFrame.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/SameZeroFrame.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/StackMapAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/StackMapFrame.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/StackMapTableAttribute.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/TopType.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/UninitializedThisType.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/UninitializedType.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/VerificationType.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/VerificationTypeFactory.java (98%) rename {src => core/src}/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java (97%) rename {src => core/src}/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java (99%) rename {src => core/src}/proguard/classfile/attribute/visitor/AllAttributeVisitor.java (98%) rename {src => core/src}/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java (97%) rename {src => core/src}/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java (96%) rename {src => core/src}/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java (97%) rename {src => core/src}/proguard/classfile/attribute/visitor/AllLineNumberInfoVisitor.java (97%) create mode 100644 core/src/proguard/classfile/attribute/visitor/AttributeCounter.java rename {src => core/src}/proguard/classfile/attribute/visitor/AttributeNameFilter.java (95%) create mode 100644 core/src/proguard/classfile/attribute/visitor/AttributeToClassVisitor.java rename {src => core/src}/proguard/classfile/attribute/visitor/AttributeVisitor.java (89%) rename {src => core/src}/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java (96%) mode change 100755 => 100644 create mode 100644 core/src/proguard/classfile/attribute/visitor/DebugAttributeVisitor.java rename {src => core/src}/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java (96%) rename {src => core/src}/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java (96%) create mode 100644 core/src/proguard/classfile/attribute/visitor/InstructionToAttributeVisitor.java rename {src => core/src}/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java (96%) rename {src => core/src}/proguard/classfile/attribute/visitor/LineNumberRangeFinder.java (97%) rename {src => core/src}/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java (96%) rename {src => core/src}/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java (96%) rename {src => core/src}/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java (80%) rename {src => core/src}/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java (93%) rename {src => core/src}/proguard/classfile/attribute/visitor/ParameterInfoVisitor.java (96%) rename {src => core/src}/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java (94%) rename {src => core/src}/proguard/classfile/attribute/visitor/StackSizeComputer.java (99%) rename {src => core/src}/proguard/classfile/attribute/visitor/package.html (100%) rename {src => core/src}/proguard/classfile/constant/ClassConstant.java (98%) rename {src => core/src}/proguard/classfile/constant/Constant.java (97%) rename {src => core/src}/proguard/classfile/constant/DoubleConstant.java (97%) rename {src => core/src}/proguard/classfile/constant/FieldrefConstant.java (97%) rename {src => core/src}/proguard/classfile/constant/FloatConstant.java (97%) rename {src => core/src}/proguard/classfile/constant/IntegerConstant.java (97%) rename {src => core/src}/proguard/classfile/constant/InterfaceMethodrefConstant.java (97%) rename {src => core/src}/proguard/classfile/constant/InvokeDynamicConstant.java (98%) mode change 100755 => 100644 rename {src => core/src}/proguard/classfile/constant/LongConstant.java (97%) rename {src => core/src}/proguard/classfile/constant/MethodHandleConstant.java (98%) mode change 100755 => 100644 rename {src => core/src}/proguard/classfile/constant/MethodTypeConstant.java (98%) rename {src => core/src}/proguard/classfile/constant/MethodrefConstant.java (97%) create mode 100644 core/src/proguard/classfile/constant/ModuleConstant.java rename {src => core/src}/proguard/classfile/constant/NameAndTypeConstant.java (98%) create mode 100644 core/src/proguard/classfile/constant/PackageConstant.java create mode 100644 core/src/proguard/classfile/constant/PrimitiveArrayConstant.java rename {src => core/src}/proguard/classfile/constant/RefConstant.java (98%) rename {src => core/src}/proguard/classfile/constant/StringConstant.java (98%) rename {src => core/src}/proguard/classfile/constant/Utf8Constant.java (99%) rename {src => core/src}/proguard/classfile/constant/visitor/AllConstantVisitor.java (96%) rename {src => core/src}/proguard/classfile/constant/visitor/BootstrapMethodArgumentVisitor.java (97%) rename {src => core/src}/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java (98%) create mode 100644 core/src/proguard/classfile/constant/visitor/ConstantCounter.java rename {src => core/src}/proguard/classfile/constant/visitor/ConstantTagFilter.java (98%) rename {src => core/src}/proguard/classfile/constant/visitor/ConstantVisitor.java (87%) rename {src => core/src}/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java (97%) rename {src => core/src}/proguard/classfile/constant/visitor/MethodrefTraveler.java (97%) create mode 100644 core/src/proguard/classfile/constant/visitor/PrimitiveArrayConstantElementVisitor.java create mode 100644 core/src/proguard/classfile/constant/visitor/PrimitiveArrayConstantVisitor.java rename {src => core/src}/proguard/classfile/constant/visitor/SuperClassConstantVisitor.java (97%) rename {src => core/src}/proguard/classfile/constant/visitor/package.html (100%) rename {src => core/src}/proguard/classfile/editor/AccessFixer.java (98%) rename {src => core/src}/proguard/classfile/editor/AnnotationAdder.java (99%) rename {src => core/src}/proguard/classfile/editor/AnnotationsAttributeEditor.java (97%) rename {src => core/src}/proguard/classfile/editor/AttributeAdder.java (99%) rename {src => core/src}/proguard/classfile/editor/AttributeSorter.java (98%) rename {src => core/src}/proguard/classfile/editor/AttributesEditor.java (99%) rename {src => core/src}/proguard/classfile/editor/BootstrapMethodInfoAdder.java (98%) rename {src => core/src}/proguard/classfile/editor/BootstrapMethodRemapper.java (67%) rename {src => core/src}/proguard/classfile/editor/BootstrapMethodsAttributeAdder.java (98%) rename {src => core/src}/proguard/classfile/editor/BootstrapMethodsAttributeEditor.java (55%) create mode 100644 core/src/proguard/classfile/editor/BootstrapMethodsAttributeShrinker.java rename {src => core/src}/proguard/classfile/editor/BridgeMethodFixer.java (98%) rename {src => core/src}/proguard/classfile/editor/ClassEditor.java (99%) rename {src => core/src}/proguard/classfile/editor/ClassElementSorter.java (97%) rename {src => core/src}/proguard/classfile/editor/ClassMemberSorter.java (97%) rename {src => core/src}/proguard/classfile/editor/ClassReferenceFixer.java (99%) rename {src => core/src}/proguard/classfile/editor/CodeAttributeComposer.java (99%) rename {src => core/src}/proguard/classfile/editor/CodeAttributeEditor.java (72%) rename {src => core/src}/proguard/classfile/editor/CodeAttributeEditorResetter.java (97%) create mode 100644 core/src/proguard/classfile/editor/CompactCodeAttributeComposer.java rename {src => core/src}/proguard/classfile/editor/ComparableConstant.java (78%) rename {src => core/src}/proguard/classfile/editor/ConstantAdder.java (93%) rename {src => core/src}/proguard/classfile/editor/ConstantPoolEditor.java (82%) rename {src => core/src}/proguard/classfile/editor/ConstantPoolRemapper.java (86%) rename {src => core/src}/proguard/classfile/editor/ConstantPoolShrinker.java (84%) rename {src => core/src}/proguard/classfile/editor/ConstantPoolSorter.java (98%) rename {src => core/src}/proguard/classfile/editor/ElementValueAdder.java (99%) rename {src => core/src}/proguard/classfile/editor/ElementValuesEditor.java (99%) rename {src => core/src}/proguard/classfile/editor/ExceptionAdder.java (97%) rename {src => core/src}/proguard/classfile/editor/ExceptionInfoAdder.java (97%) create mode 100644 core/src/proguard/classfile/editor/ExceptionInfoEditor.java rename {src => core/src}/proguard/classfile/editor/ExceptionsAttributeEditor.java (97%) rename {src => core/src}/proguard/classfile/editor/InnerClassesAccessFixer.java (97%) create mode 100644 core/src/proguard/classfile/editor/InnerClassesAttributeEditor.java rename {src => core/src}/proguard/classfile/editor/InstructionAdder.java (97%) create mode 100644 core/src/proguard/classfile/editor/InstructionSequenceBuilder.java rename {src => core/src}/proguard/classfile/editor/InstructionWriter.java (99%) rename {src => core/src}/proguard/classfile/editor/InterfaceAdder.java (97%) rename {src => core/src}/proguard/classfile/editor/InterfaceDeleter.java (99%) rename {src => core/src}/proguard/classfile/editor/InterfaceSorter.java (99%) rename {src => core/src}/proguard/classfile/editor/InterfacesEditor.java (98%) rename {src => core/src}/proguard/classfile/editor/LineNumberInfoAdder.java (98%) rename {src => core/src}/proguard/classfile/editor/LineNumberTableAttributeEditor.java (97%) rename {src => core/src}/proguard/classfile/editor/LineNumberTableAttributeTrimmer.java (98%) rename {src => core/src}/proguard/classfile/editor/LocalVariableInfoAdder.java (98%) rename {src => core/src}/proguard/classfile/editor/LocalVariableTableAttributeEditor.java (97%) rename {src => core/src}/proguard/classfile/editor/LocalVariableTypeInfoAdder.java (98%) rename {src => core/src}/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java (97%) rename {src => core/src}/proguard/classfile/editor/MemberAdder.java (79%) rename {src => core/src}/proguard/classfile/editor/MemberReferenceFixer.java (99%) create mode 100644 core/src/proguard/classfile/editor/MemberRemover.java rename {src => core/src}/proguard/classfile/editor/MethodInvocationFixer.java (99%) rename {src => core/src}/proguard/classfile/editor/NameAndTypeShrinker.java (99%) rename {src => core/src}/proguard/classfile/editor/NamedAttributeDeleter.java (97%) rename {src => core/src}/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java (97%) rename {src => core/src}/proguard/classfile/editor/ParameterInfoAdder.java (89%) create mode 100644 core/src/proguard/classfile/editor/SimplifiedClassEditor.java rename {src => core/src}/proguard/classfile/editor/StackSizeUpdater.java (96%) rename {src => core/src}/proguard/classfile/editor/SubclassAdder.java (96%) rename {src => core/src}/proguard/classfile/editor/SubclassToAdder.java (96%) rename {src => core/src}/proguard/classfile/editor/Utf8Shrinker.java (99%) rename {src => core/src}/proguard/classfile/editor/VariableCleaner.java (99%) rename {src => core/src}/proguard/classfile/editor/VariableEditor.java (98%) rename {src => core/src}/proguard/classfile/editor/VariableRemapper.java (99%) rename {src => core/src}/proguard/classfile/editor/VariableSizeUpdater.java (98%) rename {src => core/src}/proguard/classfile/editor/package.html (100%) rename {src => core/src}/proguard/classfile/instruction/BranchInstruction.java (96%) rename {src => core/src}/proguard/classfile/instruction/ConstantInstruction.java (96%) rename {src => core/src}/proguard/classfile/instruction/Instruction.java (99%) rename {src => core/src}/proguard/classfile/instruction/InstructionConstants.java (99%) rename {src => core/src}/proguard/classfile/instruction/InstructionFactory.java (99%) rename {src => core/src}/proguard/classfile/instruction/InstructionUtil.java (73%) rename {src => core/src}/proguard/classfile/instruction/LookUpSwitchInstruction.java (98%) rename {src => core/src}/proguard/classfile/instruction/SimpleInstruction.java (99%) rename {src => core/src}/proguard/classfile/instruction/SwitchInstruction.java (97%) rename {src => core/src}/proguard/classfile/instruction/TableSwitchInstruction.java (98%) rename {src => core/src}/proguard/classfile/instruction/VariableInstruction.java (99%) rename {src => core/src}/proguard/classfile/instruction/package.html (100%) rename {src => core/src}/proguard/classfile/instruction/visitor/AllInstructionVisitor.java (97%) rename {src => core/src}/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java (97%) rename {src => core/src}/proguard/classfile/instruction/visitor/InstructionCounter.java (97%) rename {src => core/src}/proguard/classfile/instruction/visitor/InstructionVisitor.java (97%) rename {src => core/src}/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java (80%) rename {src => core/src}/proguard/classfile/instruction/visitor/package.html (100%) rename {src => core/src}/proguard/classfile/io/LibraryClassReader.java (90%) rename {src => core/src}/proguard/classfile/io/ProgramClassReader.java (79%) rename {src => core/src}/proguard/classfile/io/ProgramClassWriter.java (81%) rename {src => core/src}/proguard/classfile/io/RuntimeDataInput.java (98%) rename {src => core/src}/proguard/classfile/io/RuntimeDataOutput.java (98%) rename {src => core/src}/proguard/classfile/io/package.html (100%) rename {src => core/src}/proguard/classfile/package.html (100%) rename {src => core/src}/proguard/classfile/util/AccessUtil.java (98%) rename {src => core/src}/proguard/classfile/util/AllParameterVisitor.java (85%) create mode 100644 core/src/proguard/classfile/util/ArrayInitializationMatcher.java create mode 100644 core/src/proguard/classfile/util/ArrayInitializationReplacer.java rename {src => core/src}/proguard/classfile/util/ClassReferenceInitializer.java (92%) rename {src => core/src}/proguard/classfile/util/ClassSubHierarchyInitializer.java (97%) rename {src => core/src}/proguard/classfile/util/ClassSuperHierarchyInitializer.java (93%) rename {src => core/src}/proguard/classfile/util/ClassUtil.java (71%) rename {src => core/src}/proguard/classfile/util/DescriptorClassEnumeration.java (99%) rename {src => core/src}/proguard/classfile/util/DynamicClassReferenceInitializer.java (99%) create mode 100644 core/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java rename {src => core/src}/proguard/classfile/util/EnumFieldReferenceInitializer.java (99%) rename {src => core/src}/proguard/classfile/util/ExternalTypeEnumeration.java (98%) rename {src => core/src}/proguard/classfile/util/InstructionSequenceMatcher.java (84%) rename {src => core/src}/proguard/classfile/util/InternalTypeEnumeration.java (99%) rename {src => core/src}/proguard/classfile/util/MemberFinder.java (61%) rename {src => core/src}/proguard/classfile/util/MethodLinker.java (98%) create mode 100644 core/src/proguard/classfile/util/PrimitiveArrayConstantReplacer.java rename {src => core/src}/proguard/classfile/util/SimplifiedVisitor.java (88%) rename {src => core/src}/proguard/classfile/util/StringReferenceInitializer.java (98%) rename {src => core/src}/proguard/classfile/util/StringSharer.java (99%) rename {src => core/src}/proguard/classfile/util/WarningPrinter.java (98%) rename {src => core/src}/proguard/classfile/util/package.html (100%) rename {src => core/src}/proguard/classfile/visitor/AllClassVisitor.java (96%) rename {src => core/src}/proguard/classfile/visitor/AllFieldVisitor.java (96%) rename {src => core/src}/proguard/classfile/visitor/AllMemberVisitor.java (96%) rename {src => core/src}/proguard/classfile/visitor/AllMethodVisitor.java (96%) rename {src => core/src}/proguard/classfile/visitor/BottomClassFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ClassAccessFilter.java (98%) rename {src => core/src}/proguard/classfile/visitor/ClassCleaner.java (99%) rename {src => core/src}/proguard/classfile/visitor/ClassCollector.java (96%) rename {src => core/src}/proguard/classfile/visitor/ClassCounter.java (96%) rename {src => core/src}/proguard/classfile/visitor/ClassHierarchyTraveler.java (98%) rename {src => core/src}/proguard/classfile/visitor/ClassNameFilter.java (55%) create mode 100644 core/src/proguard/classfile/visitor/ClassPoolClassVisitor.java rename {src => core/src}/proguard/classfile/visitor/ClassPoolFiller.java (96%) rename {src => core/src}/proguard/classfile/visitor/ClassPoolRemover.java (96%) rename {src => core/src}/proguard/classfile/visitor/ClassPoolVisitor.java (95%) rename {src => core/src}/proguard/classfile/visitor/ClassPresenceFilter.java (98%) rename {src => core/src}/proguard/classfile/visitor/ClassPrinter.java (84%) rename {src => core/src}/proguard/classfile/visitor/ClassVersionFilter.java (98%) rename {src => core/src}/proguard/classfile/visitor/ClassVersionSetter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ClassVisitor.java (95%) rename {src => core/src}/proguard/classfile/visitor/ConcreteClassDownTraveler.java (98%) create mode 100644 core/src/proguard/classfile/visitor/ConstructorMethodFilter.java rename {src => core/src}/proguard/classfile/visitor/DotClassClassVisitor.java (98%) rename {src => core/src}/proguard/classfile/visitor/DynamicReturnedClassVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/ExceptClassFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ExceptClassesFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ExceptionCounter.java (96%) rename {src => core/src}/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/ExceptionHandlerFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ExceptionOffsetFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ExceptionRangeFilter.java (97%) create mode 100644 core/src/proguard/classfile/visitor/FunctionalInterfaceFilter.java rename {src => core/src}/proguard/classfile/visitor/ImplementedClassConstantFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ImplementedClassFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ImplementingClassConstantFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/InitializerMethodFilter.java (98%) rename {src => core/src}/proguard/classfile/visitor/LibraryClassFilter.java (96%) rename {src => core/src}/proguard/classfile/visitor/LibraryMemberFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/MemberAccessFilter.java (95%) rename src/proguard/optimize/KeepMarker.java => core/src/proguard/classfile/visitor/MemberAccessFlagCleaner.java (50%) create mode 100644 core/src/proguard/classfile/visitor/MemberAccessFlagSetter.java rename {src => core/src}/proguard/classfile/visitor/MemberClassAccessFilter.java (98%) create mode 100644 core/src/proguard/classfile/visitor/MemberCollector.java rename {src => core/src}/proguard/classfile/visitor/MemberCounter.java (97%) rename {src => core/src}/proguard/classfile/visitor/MemberDescriptorFilter.java (72%) rename {src => core/src}/proguard/classfile/visitor/MemberDescriptorReferencedClassVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/MemberNameFilter.java (73%) rename {src => core/src}/proguard/classfile/visitor/MemberToClassVisitor.java (65%) rename {src => core/src}/proguard/classfile/visitor/MemberVisitor.java (96%) rename src/proguard/classfile/visitor/MemberCollector.java => core/src/proguard/classfile/visitor/MethodCollector.java (61%) rename {src => core/src}/proguard/classfile/visitor/MethodImplementationFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/MethodImplementationTraveler.java (98%) rename {src => core/src}/proguard/classfile/visitor/MultiClassPoolVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/MultiClassVisitor.java (68%) rename {src => core/src}/proguard/classfile/visitor/MultiMemberVisitor.java (73%) rename {src => core/src}/proguard/classfile/visitor/NamedClassVisitor.java (96%) rename {src => core/src}/proguard/classfile/visitor/NamedFieldVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/NamedMethodVisitor.java (97%) create mode 100644 core/src/proguard/classfile/visitor/ParallelAllClassVisitor.java rename {src => core/src}/proguard/classfile/visitor/ParameterVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/ProgramClassFilter.java (96%) rename {src => core/src}/proguard/classfile/visitor/ProgramMemberFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/ReferencedClassVisitor.java (99%) rename {src => core/src}/proguard/classfile/visitor/ReferencedMemberVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/SimilarMemberVisitor.java (99%) rename {src => core/src}/proguard/classfile/visitor/SimpleClassPrinter.java (82%) rename src/proguard/optimize/KeptClassFilter.java => core/src/proguard/classfile/visitor/SingleTimeClassVisitor.java (67%) rename {src => core/src}/proguard/classfile/visitor/SubclassFilter.java (97%) rename {src => core/src}/proguard/classfile/visitor/SubclassTraveler.java (96%) rename {src => core/src}/proguard/classfile/visitor/VariableClassVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/VariableMemberVisitor.java (97%) rename {src => core/src}/proguard/classfile/visitor/package.html (100%) create mode 100644 core/src/proguard/configuration/ConfigurationLogger.java create mode 100644 core/src/proguard/configuration/ConfigurationLoggingAdder.java create mode 100644 core/src/proguard/configuration/ConfigurationLoggingInstructionSequenceConstants.java create mode 100644 core/src/proguard/configuration/ConfigurationLoggingInstructionSequenceReplacer.java create mode 100644 core/src/proguard/configuration/ConfigurationLoggingInstructionSequencesReplacer.java rename {src => core/src}/proguard/evaluation/BasicBranchUnit.java (73%) create mode 100644 core/src/proguard/evaluation/BasicInvocationUnit.java rename {src => core/src}/proguard/evaluation/BranchUnit.java (97%) rename {src => core/src}/proguard/evaluation/ClassConstantValueFactory.java (94%) rename {src => core/src}/proguard/evaluation/ConstantValueFactory.java (85%) rename {src => core/src}/proguard/evaluation/InvocationUnit.java (81%) rename {src => core/src}/proguard/evaluation/Processor.java (93%) create mode 100644 core/src/proguard/evaluation/SimplifiedInvocationUnit.java rename {src => core/src}/proguard/evaluation/Stack.java (99%) rename {src => core/src}/proguard/evaluation/TracedStack.java (99%) rename {src => core/src}/proguard/evaluation/TracedVariables.java (99%) rename {src => core/src}/proguard/evaluation/Variables.java (99%) rename {src => core/src}/proguard/evaluation/value/ArrayReferenceValue.java (88%) create mode 100644 core/src/proguard/evaluation/value/ArrayReferenceValueFactory.java rename src/proguard/evaluation/value/ValueFactory.java => core/src/proguard/evaluation/value/BasicValueFactory.java (54%) rename {src => core/src}/proguard/evaluation/value/Category1Value.java (95%) rename {src => core/src}/proguard/evaluation/value/Category2Value.java (95%) rename {src => core/src}/proguard/evaluation/value/ComparisonValue.java (97%) rename {src => core/src}/proguard/evaluation/value/CompositeDoubleValue.java (97%) rename {src => core/src}/proguard/evaluation/value/CompositeFloatValue.java (97%) rename {src => core/src}/proguard/evaluation/value/CompositeIntegerValue.java (98%) rename {src => core/src}/proguard/evaluation/value/CompositeLongValue.java (96%) rename {src => core/src}/proguard/evaluation/value/ConvertedByteValue.java (96%) rename {src => core/src}/proguard/evaluation/value/ConvertedCharacterValue.java (96%) rename {src => core/src}/proguard/evaluation/value/ConvertedDoubleValue.java (96%) rename {src => core/src}/proguard/evaluation/value/ConvertedFloatValue.java (96%) rename {src => core/src}/proguard/evaluation/value/ConvertedIntegerValue.java (96%) rename {src => core/src}/proguard/evaluation/value/ConvertedLongValue.java (96%) rename {src => core/src}/proguard/evaluation/value/ConvertedShortValue.java (96%) rename {src => core/src}/proguard/evaluation/value/DetailedArrayReferenceValue.java (84%) rename src/proguard/evaluation/value/DetailedValueFactory.java => core/src/proguard/evaluation/value/DetailedArrayValueFactory.java (89%) rename {src => core/src}/proguard/evaluation/value/DoubleValue.java (99%) rename {src => core/src}/proguard/evaluation/value/FloatValue.java (99%) rename {src => core/src}/proguard/evaluation/value/IdentifiedArrayReferenceValue.java (73%) rename {src => core/src}/proguard/evaluation/value/IdentifiedDoubleValue.java (97%) rename {src => core/src}/proguard/evaluation/value/IdentifiedFloatValue.java (97%) rename {src => core/src}/proguard/evaluation/value/IdentifiedIntegerValue.java (97%) rename {src => core/src}/proguard/evaluation/value/IdentifiedLongValue.java (97%) rename {src => core/src}/proguard/evaluation/value/IdentifiedReferenceValue.java (70%) rename {src => core/src}/proguard/evaluation/value/IdentifiedValueFactory.java (82%) rename {src => core/src}/proguard/evaluation/value/InitialValueFactory.java (97%) rename {src => core/src}/proguard/evaluation/value/InstructionOffsetValue.java (53%) rename {src => core/src}/proguard/evaluation/value/IntegerValue.java (99%) rename {src => core/src}/proguard/evaluation/value/LongValue.java (99%) rename {src => core/src}/proguard/evaluation/value/NegatedDoubleValue.java (97%) rename {src => core/src}/proguard/evaluation/value/NegatedFloatValue.java (96%) rename {src => core/src}/proguard/evaluation/value/NegatedIntegerValue.java (97%) rename {src => core/src}/proguard/evaluation/value/NegatedLongValue.java (96%) rename {src => core/src}/proguard/evaluation/value/ParticularDoubleValue.java (98%) rename {src => core/src}/proguard/evaluation/value/ParticularFloatValue.java (98%) rename {src => core/src}/proguard/evaluation/value/ParticularIntegerValue.java (99%) rename {src => core/src}/proguard/evaluation/value/ParticularLongValue.java (99%) rename {src => core/src}/proguard/evaluation/value/ParticularValueFactory.java (62%) create mode 100644 core/src/proguard/evaluation/value/PrimitiveTypedReferenceValueFactory.java rename {src => core/src}/proguard/evaluation/value/ReferenceValue.java (77%) rename {src => core/src}/proguard/evaluation/value/SpecificDoubleValue.java (96%) rename {src => core/src}/proguard/evaluation/value/SpecificFloatValue.java (96%) rename {src => core/src}/proguard/evaluation/value/SpecificIntegerValue.java (98%) rename {src => core/src}/proguard/evaluation/value/SpecificLongValue.java (98%) rename {src => core/src}/proguard/evaluation/value/TopValue.java (97%) create mode 100644 core/src/proguard/evaluation/value/TracedReferenceValue.java create mode 100644 core/src/proguard/evaluation/value/TracingValue.java rename {src => core/src}/proguard/evaluation/value/TypedReferenceValue.java (60%) create mode 100644 core/src/proguard/evaluation/value/TypedReferenceValueFactory.java rename {src => core/src}/proguard/evaluation/value/UnknownDoubleValue.java (91%) rename {src => core/src}/proguard/evaluation/value/UnknownFloatValue.java (91%) rename {src => core/src}/proguard/evaluation/value/UnknownIntegerValue.java (92%) rename {src => core/src}/proguard/evaluation/value/UnknownLongValue.java (92%) create mode 100644 core/src/proguard/evaluation/value/UnknownReferenceValue.java rename {src => core/src}/proguard/evaluation/value/Value.java (98%) create mode 100644 core/src/proguard/evaluation/value/ValueFactory.java rename {src => core/src}/proguard/evaluation/value/package.html (100%) rename {src => core/src}/proguard/io/CascadingDataEntryWriter.java (78%) create mode 100644 core/src/proguard/io/ClassDataEntryWriter.java rename {src => core/src}/proguard/io/ClassFilter.java (96%) create mode 100644 core/src/proguard/io/ClassMapDataEntryWriter.java create mode 100644 core/src/proguard/io/ClassPathDataEntry.java rename {src => core/src}/proguard/io/ClassReader.java (92%) rename {src => core/src}/proguard/io/DataEntry.java (83%) rename {src => core/src}/proguard/io/DataEntryClassWriter.java (87%) rename {src => core/src}/proguard/io/DataEntryCopier.java (74%) rename {src => core/src}/proguard/io/DataEntryDirectoryFilter.java (95%) rename {src => core/src}/proguard/io/DataEntryFilter.java (95%) rename {src => core/src}/proguard/io/DataEntryNameFilter.java (96%) rename {src => core/src}/proguard/io/DataEntryParentFilter.java (96%) rename {src => core/src}/proguard/io/DataEntryPump.java (96%) rename {src => core/src}/proguard/io/DataEntryReader.java (95%) rename {src => core/src}/proguard/io/DataEntryRewriter.java (93%) rename {src => core/src}/proguard/io/DataEntryWriter.java (57%) rename {src => core/src}/proguard/io/DirectoryFilter.java (96%) rename {src => core/src}/proguard/io/DirectoryPump.java (90%) rename {src => core/src}/proguard/io/DirectoryWriter.java (53%) create mode 100644 core/src/proguard/io/ExtraDataEntryWriter.java rename {src => core/src}/proguard/io/FileDataEntry.java (71%) rename {src => core/src}/proguard/io/FilteredDataEntryReader.java (98%) rename {src => core/src}/proguard/io/FilteredDataEntryWriter.java (78%) rename {src => core/src}/proguard/io/Finisher.java (95%) create mode 100644 core/src/proguard/io/IdleRewriter.java rename {src => core/src}/proguard/io/JarReader.java (82%) create mode 100644 core/src/proguard/io/JarWriter.java rename {src => core/src}/proguard/io/ManifestRewriter.java (96%) rename {src => core/src}/proguard/io/NameFilter.java (98%) create mode 100644 core/src/proguard/io/NameFilteredDataEntryWriter.java rename {src => core/src}/proguard/io/ParentDataEntryWriter.java (70%) create mode 100644 core/src/proguard/io/PrefixAddingDataEntryWriter.java create mode 100644 core/src/proguard/io/PrefixStrippingDataEntryReader.java rename {src => core/src}/proguard/io/RenamedDataEntry.java (66%) rename src/proguard/io/DataEntryRenamer.java => core/src/proguard/io/RenamedDataEntryReader.java (71%) rename src/proguard/io/DataEntryObfuscator.java => core/src/proguard/io/RenamedDataEntryWriter.java (76%) create mode 100644 core/src/proguard/io/RenamedParentDataEntryWriter.java create mode 100644 core/src/proguard/io/WrappedDataEntry.java rename {src => core/src}/proguard/io/ZipDataEntry.java (93%) create mode 100644 core/src/proguard/io/ZipFileDataEntry.java create mode 100644 core/src/proguard/io/ZipOutput.java rename {src => core/src}/proguard/io/package.html (100%) rename {src => core/src}/proguard/obfuscate/AttributeShrinker.java (98%) rename {src => core/src}/proguard/obfuscate/AttributeUsageMarker.java (97%) rename {src => core/src}/proguard/obfuscate/ClassObfuscator.java (99%) rename {src => core/src}/proguard/obfuscate/ClassRenamer.java (98%) rename {src => core/src}/proguard/obfuscate/DictionaryNameFactory.java (54%) rename {src => core/src}/proguard/obfuscate/MapCleaner.java (96%) rename {src => core/src}/proguard/obfuscate/MappingKeeper.java (99%) rename {src => core/src}/proguard/obfuscate/MappingPrinter.java (99%) rename {src => core/src}/proguard/obfuscate/MappingProcessor.java (98%) rename {src => core/src}/proguard/obfuscate/MappingReader.java (99%) rename {src => core/src}/proguard/obfuscate/MemberNameCleaner.java (97%) rename {src => core/src}/proguard/obfuscate/MemberNameCollector.java (98%) rename {src => core/src}/proguard/obfuscate/MemberNameConflictFixer.java (99%) rename {src => core/src}/proguard/obfuscate/MemberNameFilter.java (98%) rename {src => core/src}/proguard/obfuscate/MemberObfuscator.java (99%) rename {src => core/src}/proguard/obfuscate/MemberSpecialNameFilter.java (98%) rename {src => core/src}/proguard/obfuscate/MultiMappingProcessor.java (98%) rename {src => core/src}/proguard/obfuscate/NameFactory.java (95%) rename {src => core/src}/proguard/obfuscate/NameFactoryResetter.java (96%) rename {src => core/src}/proguard/obfuscate/NameMarker.java (98%) rename {src => core/src}/proguard/obfuscate/NumericNameFactory.java (95%) rename {src => core/src}/proguard/obfuscate/Obfuscator.java (89%) rename {src => core/src}/proguard/obfuscate/ParameterNameMarker.java (98%) create mode 100644 core/src/proguard/obfuscate/PrefixingNameFactory.java create mode 100644 core/src/proguard/obfuscate/RenamedFlagSetter.java rename {src => core/src}/proguard/obfuscate/SimpleNameFactory.java (98%) rename {src => core/src}/proguard/obfuscate/SourceFileRenamer.java (98%) rename {src => core/src}/proguard/obfuscate/SpecialNameFactory.java (97%) create mode 100644 core/src/proguard/obfuscate/UniqueMemberNameFactory.java rename {src => core/src}/proguard/obfuscate/package.html (100%) rename {src => core/src}/proguard/optimize/BootstrapMethodArgumentShrinker.java (98%) rename {src => core/src}/proguard/optimize/ChangedCodePrinter.java (95%) rename {src => core/src}/proguard/optimize/ConstantMemberFilter.java (97%) rename {src => core/src}/proguard/optimize/ConstantParameterFilter.java (97%) rename {src => core/src}/proguard/optimize/DuplicateInitializerFixer.java (84%) rename {src => core/src}/proguard/optimize/DuplicateInitializerInvocationFixer.java (99%) create mode 100644 core/src/proguard/optimize/KeepMarker.java create mode 100644 core/src/proguard/optimize/KeptClassFilter.java rename {src => core/src}/proguard/optimize/KeptMemberFilter.java (97%) rename {src => core/src}/proguard/optimize/MemberDescriptorSpecializer.java (98%) rename {src => core/src}/proguard/optimize/MethodDescriptorShrinker.java (97%) rename {src => core/src}/proguard/optimize/MethodStaticizer.java (93%) rename {src => core/src}/proguard/optimize/OptimizationInfoClassFilter.java (83%) rename {src => core/src}/proguard/optimize/OptimizationInfoMemberFilter.java (55%) create mode 100644 core/src/proguard/optimize/Optimizer.java rename {src => core/src}/proguard/optimize/ParameterShrinker.java (88%) rename {src => core/src}/proguard/optimize/TailRecursionSimplifier.java (85%) rename {src => core/src}/proguard/optimize/WriteOnlyFieldFilter.java (97%) create mode 100644 core/src/proguard/optimize/evaluation/EvaluationShrinker.java rename {src => core/src}/proguard/optimize/evaluation/EvaluationSimplifier.java (80%) create mode 100644 core/src/proguard/optimize/evaluation/InitializationFinder.java create mode 100644 core/src/proguard/optimize/evaluation/InstructionUsageMarker.java rename {src => core/src}/proguard/optimize/evaluation/LivenessAnalyzer.java (76%) rename {src => core/src}/proguard/optimize/evaluation/LoadingInvocationUnit.java (84%) create mode 100644 core/src/proguard/optimize/evaluation/ParameterTracingInvocationUnit.java rename {src => core/src}/proguard/optimize/evaluation/PartialEvaluator.java (77%) create mode 100644 core/src/proguard/optimize/evaluation/ReferenceTracingInvocationUnit.java create mode 100644 core/src/proguard/optimize/evaluation/ReferenceTracingValueFactory.java rename {src => core/src}/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java (54%) rename {src => core/src}/proguard/optimize/evaluation/SimpleEnumClassChecker.java (79%) rename {src => core/src}/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java (71%) rename {src => core/src}/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java (98%) rename {src => core/src}/proguard/optimize/evaluation/SimpleEnumUseChecker.java (97%) rename {src => core/src}/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java (96%) rename {src => core/src}/proguard/optimize/evaluation/StoringInvocationUnit.java (65%) rename {src => core/src}/proguard/optimize/evaluation/TracedBranchUnit.java (69%) rename {src => core/src}/proguard/optimize/evaluation/VariableOptimizer.java (98%) rename {src => core/src}/proguard/optimize/evaluation/package.html (100%) rename {src => core/src}/proguard/optimize/info/AccessMethodMarker.java (81%) rename {src => core/src}/proguard/optimize/info/BackwardBranchMarker.java (86%) rename {src => core/src}/proguard/optimize/info/CatchExceptionMarker.java (81%) rename {src => core/src}/proguard/optimize/info/CaughtClassFilter.java (97%) rename {src => core/src}/proguard/optimize/info/CaughtClassMarker.java (81%) create mode 100644 core/src/proguard/optimize/info/ClassOptimizationInfo.java create mode 100644 core/src/proguard/optimize/info/CodeAttributeOptimizationInfo.java rename {src => core/src}/proguard/optimize/info/DotClassFilter.java (97%) rename {src => core/src}/proguard/optimize/info/DotClassMarker.java (81%) rename {src => core/src}/proguard/optimize/info/DynamicInvocationMarker.java (84%) create mode 100644 core/src/proguard/optimize/info/EscapingClassFilter.java create mode 100644 core/src/proguard/optimize/info/EscapingClassMarker.java rename {src => core/src}/proguard/optimize/info/ExceptionInstructionChecker.java (73%) create mode 100644 core/src/proguard/optimize/info/FieldOptimizationInfo.java rename {src => core/src}/proguard/optimize/info/InstanceofClassFilter.java (97%) rename {src => core/src}/proguard/optimize/info/InstanceofClassMarker.java (80%) rename {src => core/src}/proguard/optimize/info/InstantiationClassFilter.java (97%) rename {src => core/src}/proguard/optimize/info/InstantiationClassMarker.java (80%) rename {src => core/src}/proguard/optimize/info/MethodInvocationMarker.java (84%) create mode 100644 core/src/proguard/optimize/info/MethodOptimizationInfo.java create mode 100644 core/src/proguard/optimize/info/MutableBoolean.java create mode 100644 core/src/proguard/optimize/info/NoEscapingParametersMethodMarker.java create mode 100644 core/src/proguard/optimize/info/NoExternalReturnValuesMethodMarker.java create mode 100644 core/src/proguard/optimize/info/NoExternalSideEffectMethodMarker.java rename src/proguard/optimize/info/StaticInitializerContainingClassMarker.java => core/src/proguard/optimize/info/NoSideEffectClassMarker.java (56%) rename {src => core/src}/proguard/optimize/info/NoSideEffectMethodMarker.java (68%) rename {src => core/src}/proguard/optimize/info/NonEmptyStackReturnMarker.java (89%) rename {src => core/src}/proguard/optimize/info/NonPrivateMemberMarker.java (71%) create mode 100644 core/src/proguard/optimize/info/OptimizationCodeAttributeFilter.java rename {src => core/src}/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java (84%) rename {src => core/src}/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java (89%) create mode 100644 core/src/proguard/optimize/info/ParameterEscapeMarker.java create mode 100644 core/src/proguard/optimize/info/ParameterEscapedMarker.java rename {src => core/src}/proguard/optimize/info/ParameterUsageMarker.java (71%) rename src/proguard/optimize/info/ClassOptimizationInfo.java => core/src/proguard/optimize/info/ProgramClassOptimizationInfo.java (56%) create mode 100644 core/src/proguard/optimize/info/ProgramClassOptimizationInfoSetter.java rename src/proguard/optimize/info/FieldOptimizationInfo.java => core/src/proguard/optimize/info/ProgramFieldOptimizationInfo.java (65%) create mode 100644 core/src/proguard/optimize/info/ProgramMemberOptimizationInfoSetter.java create mode 100644 core/src/proguard/optimize/info/ProgramMethodOptimizationInfo.java rename {src => core/src}/proguard/optimize/info/ReadWriteFieldMarker.java (74%) create mode 100644 core/src/proguard/optimize/info/ReferenceEscapeChecker.java create mode 100644 core/src/proguard/optimize/info/RepeatedClassPoolVisitor.java create mode 100644 core/src/proguard/optimize/info/SideEffectClassChecker.java rename src/proguard/optimize/info/StaticInitializerContainingClassFilter.java => core/src/proguard/optimize/info/SideEffectClassFilter.java (78%) rename src/proguard/optimize/info/ClassOptimizationInfoSetter.java => core/src/proguard/optimize/info/SideEffectClassMarker.java (62%) rename {src => core/src}/proguard/optimize/info/SideEffectInstructionChecker.java (76%) rename {src => core/src}/proguard/optimize/info/SideEffectMethodFilter.java (97%) create mode 100644 core/src/proguard/optimize/info/SideEffectMethodMarker.java rename {src => core/src}/proguard/optimize/info/SimpleEnumFilter.java (98%) rename {src => core/src}/proguard/optimize/info/SimpleEnumMarker.java (82%) rename {src => core/src}/proguard/optimize/info/SuperInvocationMarker.java (87%) create mode 100644 core/src/proguard/optimize/info/SynchronizedBlockMethodMarker.java rename src/proguard/optimize/info/MemberOptimizationInfoSetter.java => core/src/proguard/optimize/info/UnusedParameterMethodFilter.java (56%) create mode 100644 core/src/proguard/optimize/info/UnusedParameterOptimizationInfoUpdater.java create mode 100644 core/src/proguard/optimize/info/UsedParameterFilter.java rename {src => core/src}/proguard/optimize/info/VariableUsageMarker.java (94%) create mode 100644 core/src/proguard/optimize/info/WrapperClassMarker.java rename {src => core/src}/proguard/optimize/info/package.html (100%) rename {src => core/src}/proguard/optimize/package.html (100%) rename {src/proguard/evaluation => core/src/proguard/optimize/peephole}/BranchTargetFinder.java (81%) rename {src => core/src}/proguard/optimize/peephole/ClassFinalizer.java (97%) rename {src => core/src}/proguard/optimize/peephole/ClassMerger.java (81%) rename {src => core/src}/proguard/optimize/peephole/GotoCommonCodeReplacer.java (98%) rename {src => core/src}/proguard/optimize/peephole/GotoGotoReplacer.java (98%) rename {src => core/src}/proguard/optimize/peephole/GotoReturnReplacer.java (98%) rename {src => core/src}/proguard/optimize/peephole/HorizontalClassMerger.java (89%) create mode 100644 core/src/proguard/optimize/peephole/InstructionSequenceConstants.java create mode 100644 core/src/proguard/optimize/peephole/InstructionSequenceReplacer.java rename {src => core/src}/proguard/optimize/peephole/InstructionSequencesReplacer.java (77%) rename {src => core/src}/proguard/optimize/peephole/LineNumberLinearizer.java (99%) rename {src => core/src}/proguard/optimize/peephole/MemberPrivatizer.java (98%) rename {src => core/src}/proguard/optimize/peephole/MethodFinalizer.java (98%) rename {src => core/src}/proguard/optimize/peephole/MethodInliner.java (84%) rename {src => core/src}/proguard/optimize/peephole/NopRemover.java (98%) rename {src => core/src}/proguard/optimize/peephole/PeepholeOptimizer.java (96%) rename {src => core/src}/proguard/optimize/peephole/ReachableCodeMarker.java (99%) rename {src => core/src}/proguard/optimize/peephole/RetargetedClassFilter.java (98%) rename {src => core/src}/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java (99%) rename {src => core/src}/proguard/optimize/peephole/TargetClassChanger.java (99%) rename {src => core/src}/proguard/optimize/peephole/UnreachableCodeRemover.java (98%) rename {src => core/src}/proguard/optimize/peephole/UnreachableExceptionRemover.java (98%) rename {src => core/src}/proguard/optimize/peephole/VariableShrinker.java (98%) rename {src => core/src}/proguard/optimize/peephole/VerticalClassMerger.java (88%) create mode 100644 core/src/proguard/optimize/peephole/WildcardConstantFilter.java create mode 100644 core/src/proguard/optimize/peephole/WrapperClassMerger.java create mode 100644 core/src/proguard/optimize/peephole/WrapperClassUseSimplifier.java rename {src => core/src}/proguard/optimize/peephole/package.html (100%) rename {src => core/src}/proguard/package.html (100%) rename {src => core/src}/proguard/preverify/CodePreverifier.java (86%) rename {src => core/src}/proguard/preverify/CodeSubroutineInliner.java (99%) rename {src => core/src}/proguard/preverify/Preverifier.java (97%) rename {src => core/src}/proguard/preverify/SubroutineInliner.java (94%) rename {src => core/src}/proguard/shrink/AnnotationUsageMarker.java (99%) rename {src => core/src}/proguard/shrink/ClassShrinker.java (96%) rename {src => core/src}/proguard/shrink/InnerUsageMarker.java (98%) rename {src => core/src}/proguard/shrink/InterfaceUsageMarker.java (98%) rename {src => core/src}/proguard/shrink/LocalVariableTypeUsageMarker.java (99%) rename {src => core/src}/proguard/shrink/ShortestUsageMark.java (98%) rename {src => core/src}/proguard/shrink/ShortestUsageMarker.java (99%) rename {src => core/src}/proguard/shrink/ShortestUsagePrinter.java (99%) rename {src => core/src}/proguard/shrink/Shrinker.java (87%) rename {src => core/src}/proguard/shrink/UsageMarker.java (91%) rename {src => core/src}/proguard/shrink/UsagePrinter.java (99%) rename {src => core/src}/proguard/shrink/UsedClassFilter.java (97%) rename {src => core/src}/proguard/shrink/UsedMemberFilter.java (98%) rename {src => core/src}/proguard/shrink/package.html (100%) rename {src => core/src}/proguard/util/AndMatcher.java (79%) rename {src => core/src}/proguard/util/ArrayUtil.java (55%) create mode 100644 core/src/proguard/util/ClassNameParser.java create mode 100644 core/src/proguard/util/CollectionMatcher.java rename {src => core/src}/proguard/util/ConstantMatcher.java (90%) create mode 100644 core/src/proguard/util/Counter.java rename {src => core/src}/proguard/util/EmptyStringMatcher.java (85%) rename {src => core/src}/proguard/util/ExtensionMatcher.java (73%) rename {src => core/src}/proguard/util/FileNameParser.java (98%) rename {src => core/src}/proguard/util/FixedStringMatcher.java (65%) rename {src => core/src}/proguard/util/ListMatcher.java (88%) rename {src => core/src}/proguard/util/ListParser.java (98%) rename {src => core/src}/proguard/util/ListUtil.java (98%) create mode 100644 core/src/proguard/util/MatchedStringMatcher.java create mode 100644 core/src/proguard/util/MultiValueMap.java create mode 100644 core/src/proguard/util/NameParser.java rename {src => core/src}/proguard/util/NotMatcher.java (85%) rename {src => core/src}/proguard/util/ObjectUtil.java (97%) rename {src => core/src}/proguard/util/OrMatcher.java (79%) create mode 100644 core/src/proguard/util/PrintWriterUtil.java rename {src => core/src}/proguard/util/SettableMatcher.java (86%) rename {src => core/src}/proguard/util/StringMatcher.java (74%) rename {src => core/src}/proguard/util/StringParser.java (95%) create mode 100644 core/src/proguard/util/StringTransformer.java create mode 100644 core/src/proguard/util/StringUtil.java create mode 100644 core/src/proguard/util/VariableStringMatcher.java rename {src => core/src}/proguard/util/package.html (100%) create mode 100644 examples/android/AndroidManifest.xml create mode 100644 examples/android/build.gradle create mode 100644 examples/android/debug.keystore create mode 100644 examples/android/local.properties create mode 100644 examples/android/proguard-project.txt create mode 100644 examples/android/res/drawable/ic_launcher.png create mode 100644 examples/android/res/values/colors.xml create mode 100644 examples/android/res/values/strings.xml create mode 100644 examples/android/res/values/styles.xml create mode 100644 examples/android/src/com/example/HelloWorldActivity.java delete mode 100644 examples/annotations/lib/annotations.jar delete mode 100644 examples/annotations/lib/annotations.pro rename examples/{ => standalone}/android.pro (62%) rename examples/{ => standalone}/applets.pro (80%) rename examples/{ => standalone}/applications.pro (82%) rename examples/{ => standalone}/library.pro (84%) rename examples/{ => standalone}/midlets.pro (91%) rename examples/{ => standalone}/proguard.pro (69%) rename examples/{ => standalone}/proguardall.pro (66%) rename examples/{ => standalone}/proguardgui.pro (60%) rename examples/{ => standalone}/retrace.pro (66%) rename examples/{ => standalone}/scala.pro (89%) rename examples/{ => standalone}/servlets.pro (83%) create mode 100644 gradle/ant.properties create mode 100644 gradle/build.gradle create mode 100755 gradle/build.sh create mode 100644 gradle/build.xml create mode 100644 gradle/makefile rename {buildscripts/maven/gradle => gradle}/pom.xml (80%) create mode 100644 gradle/settings.gradle rename {src => gradle/src}/proguard/gradle/ProGuardTask.java (91%) create mode 100644 gui/build.gradle create mode 100755 gui/build.sh create mode 100644 gui/build.xml create mode 100644 gui/makefile rename {buildscripts/maven/gui => gui}/pom.xml (69%) create mode 100644 gui/settings.gradle rename {src/proguard/gui => gui/src/META-INF}/MANIFEST.MF (100%) rename {src => gui/src}/proguard/gui/ClassPathPanel.java (98%) rename {src => gui/src}/proguard/gui/ClassSpecificationDialog.java (93%) rename {src => gui/src}/proguard/gui/ClassSpecificationsPanel.java (99%) rename {src => gui/src}/proguard/gui/ExtensionFileFilter.java (97%) rename {src => gui/src}/proguard/gui/FilterBuilder.java (99%) rename {src => gui/src}/proguard/gui/FilterDialog.java (73%) rename {src => gui/src}/proguard/gui/GUIResources.java (96%) rename {src => gui/src}/proguard/gui/GUIResources.properties (92%) rename {src => gui/src}/proguard/gui/KeepSpecificationsPanel.java (92%) rename {src => gui/src}/proguard/gui/ListPanel.java (99%) rename {src => gui/src}/proguard/gui/MemberSpecificationDialog.java (99%) rename {src => gui/src}/proguard/gui/MemberSpecificationsPanel.java (99%) rename {src => gui/src}/proguard/gui/MessageDialogRunnable.java (98%) rename {src => gui/src}/proguard/gui/OptimizationsDialog.java (99%) rename {src => gui/src}/proguard/gui/ProGuardGUI.java (77%) rename {src => gui/src}/proguard/gui/ProGuardRunnable.java (89%) rename {src => gui/src}/proguard/gui/ReTraceRunnable.java (98%) rename {src => gui/src}/proguard/gui/SwingUtil.java (97%) rename {src => gui/src}/proguard/gui/TabbedPane.java (99%) rename {src => gui/src}/proguard/gui/TextAreaOutputStream.java (97%) rename {src => gui/src}/proguard/gui/TextAreaWriter.java (97%) rename {src => gui/src}/proguard/gui/arrow.gif (100%) rename {src => gui/src}/proguard/gui/boilerplate.pro (51%) rename {src => gui/src}/proguard/gui/default.pro (100%) rename {src => gui/src}/proguard/gui/package.html (100%) rename {src => gui/src}/proguard/gui/splash/BufferedSprite.java (98%) rename {src => gui/src}/proguard/gui/splash/CircleSprite.java (97%) rename {src => gui/src}/proguard/gui/splash/ClipSprite.java (98%) rename {src => gui/src}/proguard/gui/splash/ColorSprite.java (97%) rename {src => gui/src}/proguard/gui/splash/CompositeSprite.java (96%) rename {src => gui/src}/proguard/gui/splash/ConstantColor.java (95%) rename {src => gui/src}/proguard/gui/splash/ConstantDouble.java (95%) rename {src => gui/src}/proguard/gui/splash/ConstantFont.java (95%) rename {src => gui/src}/proguard/gui/splash/ConstantInt.java (95%) rename {src => gui/src}/proguard/gui/splash/ConstantString.java (95%) rename {src => gui/src}/proguard/gui/splash/ConstantTiming.java (96%) rename {src => gui/src}/proguard/gui/splash/FontSprite.java (97%) rename {src => gui/src}/proguard/gui/splash/ImageSprite.java (97%) rename {src => gui/src}/proguard/gui/splash/LinearColor.java (97%) rename {src => gui/src}/proguard/gui/splash/LinearDouble.java (96%) rename {src => gui/src}/proguard/gui/splash/LinearInt.java (96%) rename {src => gui/src}/proguard/gui/splash/LinearTiming.java (96%) rename {src => gui/src}/proguard/gui/splash/OverrideGraphics2D.java (99%) rename {src => gui/src}/proguard/gui/splash/RectangleSprite.java (98%) rename {src => gui/src}/proguard/gui/splash/SawToothTiming.java (96%) rename {src => gui/src}/proguard/gui/splash/ShadowedSprite.java (98%) rename {src => gui/src}/proguard/gui/splash/SineTiming.java (96%) rename {src => gui/src}/proguard/gui/splash/SmoothTiming.java (97%) rename {src => gui/src}/proguard/gui/splash/SplashPanel.java (99%) rename {src => gui/src}/proguard/gui/splash/Sprite.java (95%) rename {src => gui/src}/proguard/gui/splash/TextSprite.java (98%) rename {src => gui/src}/proguard/gui/splash/TimeSwitchSprite.java (97%) rename {src => gui/src}/proguard/gui/splash/Timing.java (95%) rename {src => gui/src}/proguard/gui/splash/TypeWriterString.java (97%) rename {src => gui/src}/proguard/gui/splash/VariableColor.java (95%) rename {src => gui/src}/proguard/gui/splash/VariableDouble.java (95%) rename {src => gui/src}/proguard/gui/splash/VariableFont.java (95%) rename {src => gui/src}/proguard/gui/splash/VariableInt.java (95%) rename {src => gui/src}/proguard/gui/splash/VariableSizeFont.java (96%) rename {src => gui/src}/proguard/gui/splash/VariableString.java (95%) rename {src => gui/src}/proguard/gui/splash/package.html (100%) rename {src => gui/src}/proguard/gui/vtitle.png (100%) create mode 100644 lib/annotations.jar create mode 100644 lib/proguard.jar create mode 100644 lib/proguardgui.jar create mode 100644 lib/retrace.jar create mode 100644 retrace/build.gradle create mode 100755 retrace/build.sh create mode 100644 retrace/build.xml create mode 100644 retrace/makefile rename {buildscripts/maven/retrace => retrace}/pom.xml (70%) create mode 100644 retrace/settings.gradle rename {src/proguard/retrace => retrace/src/META-INF}/MANIFEST.MF (100%) rename {src => retrace/src}/proguard/retrace/FrameInfo.java (97%) rename {src => retrace/src}/proguard/retrace/FramePattern.java (99%) rename {src => retrace/src}/proguard/retrace/FrameRemapper.java (99%) rename {src => retrace/src}/proguard/retrace/ReTrace.java (99%) rename {src => retrace/src}/proguard/retrace/package.html (100%) delete mode 100644 src/proguard/ClassSpecificationVisitorFactory.java delete mode 100644 src/proguard/DataEntryWriterFactory.java delete mode 100644 src/proguard/OutputWriter.java delete mode 100644 src/proguard/classfile/util/DynamicMemberReferenceInitializer.java delete mode 100644 src/proguard/evaluation/BasicInvocationUnit.java delete mode 100644 src/proguard/io/ClassRewriter.java delete mode 100644 src/proguard/io/JarWriter.java delete mode 100644 src/proguard/optimize/Optimizer.java delete mode 100644 src/proguard/optimize/evaluation/EvaluationShrinker.java delete mode 100644 src/proguard/optimize/info/MethodOptimizationInfo.java delete mode 100644 src/proguard/optimize/info/SideEffectMethodMarker.java delete mode 100644 src/proguard/optimize/peephole/InstructionSequenceConstants.java delete mode 100644 src/proguard/optimize/peephole/InstructionSequenceReplacer.java delete mode 100644 src/proguard/util/ClassNameParser.java delete mode 100644 src/proguard/util/NameParser.java delete mode 100644 src/proguard/util/VariableStringMatcher.java create mode 100644 wtk/ant.properties create mode 100644 wtk/build.gradle create mode 100755 wtk/build.sh create mode 100644 wtk/build.xml create mode 100644 wtk/gradle.properties create mode 100644 wtk/makefile rename {buildscripts/maven/wtk => wtk}/pom.xml (70%) create mode 100644 wtk/settings.gradle rename {src => wtk/src}/proguard/wtk/ProGuardObfuscator.java (98%) rename {src => wtk/src}/proguard/wtk/default.pro (100%) rename {src => wtk/src}/proguard/wtk/package.html (100%) diff --git a/README b/README index 1b3a54f8c..ec047fdaa 100644 --- a/README +++ b/README @@ -7,7 +7,16 @@ This distribution contains the following directories: - 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 + +It also contains the source code and builds scripts: + +- core : the ProGuard core +- retrace : the ReTrace tool +- gui : the ProGuard/ReTrace GUI +- gradle : the ProGuard Gradle plugin +- ant : the ProGuard Ant plugin +- wtk : the ProGuard WTK plugin +- annotations : the optional annotations to configure ProGuard - buildscripts : various alternative build scripts @@ -20,14 +29,14 @@ 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 + cd examples/standalone + ../../bin/proguard.sh @ proguard.pro The resulting proguard_out.jar contains the same application, but it's a lot smaller. Enjoy! -http://proguard.sourceforge.net/ +https://www.guardsquare.com/proguard -Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare +Copyright (c) 2002-2018 Eric Lafortune @ GuardSquare diff --git a/annotations/build.gradle b/annotations/build.gradle new file mode 100644 index 000000000..588ddbb9e --- /dev/null +++ b/annotations/build.gradle @@ -0,0 +1,16 @@ +// Gradle build script for the ProGuard annotations. + +apply plugin: 'java' + +sourceSets.main { + java { + srcDirs = ['src'] + } + resources { + srcDirs = ['src'] + include '**/*.properties' + include '**/*.gif' + include '**/*.png' + include '**/*.pro' + } +} diff --git a/annotations/build.sh b/annotations/build.sh new file mode 100755 index 000000000..004251b9a --- /dev/null +++ b/annotations/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# GNU/Linux build script for ProGuard. + +cd $(dirname "$0") + +source ../buildscripts/functions.sh + +MAIN_CLASS=proguard.annotation.* + +compile $MAIN_CLASS && \ +createjar "$ANNOTATIONS_JAR" || exit 1 diff --git a/annotations/build.xml b/annotations/build.xml new file mode 100644 index 000000000..e6dc8a939 --- /dev/null +++ b/annotations/build.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/annotations/makefile b/annotations/makefile new file mode 100644 index 000000000..ef2ce97cc --- /dev/null +++ b/annotations/makefile @@ -0,0 +1,7 @@ +# GNU/Linux makefile for the ProGuard annotations. + +MAIN_CLASS = proguard/annotation/* +CLASSPATH = +TARGET = annotations + +include ../buildscripts/functions.mk diff --git a/annotations/pom.xml b/annotations/pom.xml new file mode 100644 index 000000000..4e4a32eb5 --- /dev/null +++ b/annotations/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + net.sf.proguard + proguard-parent + 6.0 + ../buildscripts/pom.xml + + proguard-annotations + [${project.groupId}] ${project.artifactId} + + + src + + + maven-source-plugin + + + maven-compiler-plugin + + + maven-jar-plugin + + + maven-javadoc-plugin + + + + diff --git a/examples/annotations/src/proguard/annotation/Keep.java b/annotations/src/proguard/annotation/Keep.java similarity index 100% rename from examples/annotations/src/proguard/annotation/Keep.java rename to annotations/src/proguard/annotation/Keep.java diff --git a/examples/annotations/src/proguard/annotation/KeepApplication.java b/annotations/src/proguard/annotation/KeepApplication.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepApplication.java rename to annotations/src/proguard/annotation/KeepApplication.java diff --git a/examples/annotations/src/proguard/annotation/KeepClassMemberNames.java b/annotations/src/proguard/annotation/KeepClassMemberNames.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepClassMemberNames.java rename to annotations/src/proguard/annotation/KeepClassMemberNames.java diff --git a/examples/annotations/src/proguard/annotation/KeepClassMembers.java b/annotations/src/proguard/annotation/KeepClassMembers.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepClassMembers.java rename to annotations/src/proguard/annotation/KeepClassMembers.java diff --git a/examples/annotations/src/proguard/annotation/KeepGettersSetters.java b/annotations/src/proguard/annotation/KeepGettersSetters.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepGettersSetters.java rename to annotations/src/proguard/annotation/KeepGettersSetters.java diff --git a/examples/annotations/src/proguard/annotation/KeepImplementations.java b/annotations/src/proguard/annotation/KeepImplementations.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepImplementations.java rename to annotations/src/proguard/annotation/KeepImplementations.java diff --git a/examples/annotations/src/proguard/annotation/KeepName.java b/annotations/src/proguard/annotation/KeepName.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepName.java rename to annotations/src/proguard/annotation/KeepName.java diff --git a/examples/annotations/src/proguard/annotation/KeepPublicClassMemberNames.java b/annotations/src/proguard/annotation/KeepPublicClassMemberNames.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepPublicClassMemberNames.java rename to annotations/src/proguard/annotation/KeepPublicClassMemberNames.java diff --git a/examples/annotations/src/proguard/annotation/KeepPublicClassMembers.java b/annotations/src/proguard/annotation/KeepPublicClassMembers.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepPublicClassMembers.java rename to annotations/src/proguard/annotation/KeepPublicClassMembers.java diff --git a/examples/annotations/src/proguard/annotation/KeepPublicGettersSetters.java b/annotations/src/proguard/annotation/KeepPublicGettersSetters.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepPublicGettersSetters.java rename to annotations/src/proguard/annotation/KeepPublicGettersSetters.java diff --git a/examples/annotations/src/proguard/annotation/KeepPublicImplementations.java b/annotations/src/proguard/annotation/KeepPublicImplementations.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepPublicImplementations.java rename to annotations/src/proguard/annotation/KeepPublicImplementations.java diff --git a/examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java b/annotations/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java rename to annotations/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java diff --git a/examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMembers.java b/annotations/src/proguard/annotation/KeepPublicProtectedClassMembers.java similarity index 100% rename from examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMembers.java rename to annotations/src/proguard/annotation/KeepPublicProtectedClassMembers.java diff --git a/ant/build.gradle b/ant/build.gradle new file mode 100644 index 000000000..f5db0adbc --- /dev/null +++ b/ant/build.gradle @@ -0,0 +1,25 @@ +// Gradle build script for the ProGuard Ant task. + +apply plugin: 'java' + +repositories { + jcenter() +} + +sourceSets.main { + java { + srcDirs = ['src'] + } + resources { + srcDirs = ['src'] + include '**/*.properties' + include '**/*.gif' + include '**/*.png' + include '**/*.pro' + } +} + +dependencies { + compile project(':core') + compile 'org.apache.ant:ant:1.9.7' +} diff --git a/ant/build.sh b/ant/build.sh new file mode 100755 index 000000000..cc2663400 --- /dev/null +++ b/ant/build.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# +# GNU/Linux build script for the ProGuard Ant task. + +cd $(dirname "$0") + +source ../buildscripts/functions.sh + +MAIN_CLASS=proguard.ant.ProGuardTask + +ANT_HOME=${ANT_HOME:-/usr/local/java/ant} + +ANT_JAR=$ANT_HOME/lib/ant.jar + +# Make sure the Ant jar is present. +if [ ! -f "$ANT_JAR" ]; then + echo "Please make sure the environment variable ANT_HOME is set correctly," + echo "if you want to compile the optional ProGuard Ant task." + exit 1 +fi + +# Make sure the ProGuard core has been compiled. +if [ ! -d ../core/$OUT ]; then + ../core/build.sh || exit 1 +fi + +# Compile and package. +export CLASSPATH=../core/$OUT:$ANT_JAR + +compile $MAIN_CLASS && \ +updatejar "$PROGUARD_JAR" || exit 1 diff --git a/ant/build.xml b/ant/build.xml new file mode 100644 index 000000000..79872e9c1 --- /dev/null +++ b/ant/build.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ant/makefile b/ant/makefile new file mode 100644 index 000000000..feb0883e8 --- /dev/null +++ b/ant/makefile @@ -0,0 +1,15 @@ +# GNU/Linux makefile for the ProGuard Ant task. + +ifndef ANT_HOME +ANT_HOME = /usr/local/java/ant +endif + +MAIN_CLASS = proguard/ant/ProGuardTask +ANT_JAR = $(ANT_HOME)/lib/ant.jar +CLASSPATH = ../core/$(OUT):$(ANT_JAR) +TARGET = proguard +UPDATE_JAR = true + +include ../buildscripts/functions.mk + +$(ANT_JAR): ; $(error Please make sure ANT_HOME is set correctly) diff --git a/buildscripts/maven/ant/pom.xml b/ant/pom.xml similarity index 70% rename from buildscripts/maven/ant/pom.xml rename to ant/pom.xml index 5d7a8fdd5..22c4d335d 100644 --- a/buildscripts/maven/ant/pom.xml +++ b/ant/pom.xml @@ -1,4 +1,5 @@ + net.sf.proguard proguard-parent - 5.3.3 - ../pom.xml + 6.0 + ../buildscripts/pom.xml proguard-anttask [${project.groupId}] ${project.artifactId} - ../../../src + src maven-source-plugin - - - proguard/ant/** - - maven-compiler-plugin - - - proguard/ant/**.java - - maven-jar-plugin @@ -44,17 +35,11 @@ maven-javadoc-plugin - - proguard.ant - - ../../../src - - proguard/ant/** - + src **/*.java diff --git a/ant/settings.gradle b/ant/settings.gradle new file mode 100644 index 000000000..55d8d75b9 --- /dev/null +++ b/ant/settings.gradle @@ -0,0 +1 @@ +includeFlat 'core' diff --git a/src/proguard/ant/ClassPathElement.java b/ant/src/proguard/ant/ClassPathElement.java similarity index 99% rename from src/proguard/ant/ClassPathElement.java rename to ant/src/proguard/ant/ClassPathElement.java index e87fb4ce6..830a93605 100644 --- a/src/proguard/ant/ClassPathElement.java +++ b/ant/src/proguard/ant/ClassPathElement.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/ant/ClassSpecificationElement.java b/ant/src/proguard/ant/ClassSpecificationElement.java similarity index 96% rename from src/proguard/ant/ClassSpecificationElement.java rename to ant/src/proguard/ant/ClassSpecificationElement.java index a765b1856..6b80115b9 100644 --- a/src/proguard/ant/ClassSpecificationElement.java +++ b/ant/src/proguard/ant/ClassSpecificationElement.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -219,11 +219,11 @@ private int requiredAccessFlags(boolean set, token; int accessFlag = - strippedToken.equals(JavaConstants.ACC_PUBLIC) ? ClassConstants.ACC_PUBLIC : - strippedToken.equals(JavaConstants.ACC_FINAL) ? ClassConstants.ACC_FINAL : - strippedToken.equals(JavaConstants.ACC_ABSTRACT) ? ClassConstants.ACC_ABSTRACT : - strippedToken.equals(JavaConstants.ACC_SYNTHETIC) ? ClassConstants.ACC_SYNTHETIC : - strippedToken.equals(JavaConstants.ACC_ANNOTATION) ? ClassConstants.ACC_ANNOTATTION : + strippedToken.equals(JavaConstants.ACC_PUBLIC) ? ClassConstants.ACC_PUBLIC : + strippedToken.equals(JavaConstants.ACC_FINAL) ? ClassConstants.ACC_FINAL : + strippedToken.equals(JavaConstants.ACC_ABSTRACT) ? ClassConstants.ACC_ABSTRACT : + strippedToken.equals(JavaConstants.ACC_SYNTHETIC) ? ClassConstants.ACC_SYNTHETIC : + strippedToken.equals(JavaConstants.ACC_ANNOTATION) ? ClassConstants.ACC_ANNOTATION : 0; if (accessFlag == 0) diff --git a/src/proguard/ant/ConfigurationElement.java b/ant/src/proguard/ant/ConfigurationElement.java similarity index 98% rename from src/proguard/ant/ConfigurationElement.java rename to ant/src/proguard/ant/ConfigurationElement.java index e98cbee1e..0e5374305 100644 --- a/src/proguard/ant/ConfigurationElement.java +++ b/ant/src/proguard/ant/ConfigurationElement.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/ant/ConfigurationTask.java b/ant/src/proguard/ant/ConfigurationTask.java similarity index 76% rename from src/proguard/ant/ConfigurationTask.java rename to ant/src/proguard/ant/ConfigurationTask.java index 44979acba..28f6715df 100644 --- a/src/proguard/ant/ConfigurationTask.java +++ b/ant/src/proguard/ant/ConfigurationTask.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -43,47 +43,56 @@ public class ConfigurationTask extends Task public void appendTo(Configuration configuration) { // Append all of these configuration entries to the given configuration. - configuration.programJars = extendClassPath(configuration.programJars, - this.configuration.programJars); + configuration.programJars = extendClassPath(configuration.programJars, + this.configuration.programJars); - configuration.libraryJars = extendClassPath(configuration.libraryJars, - this.configuration.libraryJars); + configuration.libraryJars = extendClassPath(configuration.libraryJars, + this.configuration.libraryJars); - configuration.keep = extendClassSpecifications(configuration.keep, - this.configuration.keep); + configuration.keep = extendClassSpecifications(configuration.keep, + this.configuration.keep); - configuration.keepDirectories = extendList(configuration.keepDirectories, - this.configuration.keepDirectories); + configuration.keepDirectories = extendList(configuration.keepDirectories, + this.configuration.keepDirectories); - configuration.whyAreYouKeeping = extendClassSpecifications(configuration.whyAreYouKeeping, - this.configuration.whyAreYouKeeping); + configuration.whyAreYouKeeping = extendClassSpecifications(configuration.whyAreYouKeeping, + this.configuration.whyAreYouKeeping); - configuration.optimizations = extendClassSpecifications(configuration.optimizations, - this.configuration.optimizations); + configuration.optimizations = extendClassSpecifications(configuration.optimizations, + this.configuration.optimizations); - configuration.assumeNoSideEffects = extendClassSpecifications(configuration.assumeNoSideEffects, - this.configuration.assumeNoSideEffects); + configuration.assumeNoSideEffects = extendClassSpecifications(configuration.assumeNoSideEffects, + this.configuration.assumeNoSideEffects); - configuration.keepPackageNames = extendList(configuration.keepPackageNames, - this.configuration.keepPackageNames); + configuration.assumeNoExternalSideEffects = extendClassSpecifications(configuration.assumeNoExternalSideEffects, + this.configuration.assumeNoExternalSideEffects); - configuration.keepAttributes = extendList(configuration.keepAttributes, - this.configuration.keepAttributes); + configuration.assumeNoEscapingParameters = extendClassSpecifications(configuration.assumeNoEscapingParameters, + this.configuration.assumeNoEscapingParameters); - configuration.adaptClassStrings = extendList(configuration.adaptClassStrings, - this.configuration.adaptClassStrings); + configuration.assumeNoExternalReturnValues = extendClassSpecifications(configuration.assumeNoExternalReturnValues, + this.configuration.assumeNoExternalReturnValues); - configuration.adaptResourceFileNames = extendList(configuration.adaptResourceFileNames, - this.configuration.adaptResourceFileNames); + configuration.keepPackageNames = extendList(configuration.keepPackageNames, + this.configuration.keepPackageNames); - configuration.adaptResourceFileContents = extendList(configuration.adaptResourceFileContents, - this.configuration.adaptResourceFileContents); + configuration.keepAttributes = extendList(configuration.keepAttributes, + this.configuration.keepAttributes); - configuration.note = extendList(configuration.note, - this.configuration.note); + configuration.adaptClassStrings = extendList(configuration.adaptClassStrings, + this.configuration.adaptClassStrings); - configuration.warn = extendList(configuration.warn, - this.configuration.warn); + 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); } @@ -204,6 +213,27 @@ public void addConfiguredAssumenosideeffects(ClassSpecificationElement classSpec } + public void addConfiguredAssumenoexternalsideeffects(ClassSpecificationElement classSpecificationElement) + { + configuration.assumeNoExternalSideEffects = extendClassSpecifications(configuration.assumeNoExternalSideEffects, + classSpecificationElement); + } + + + public void addConfiguredAssumenoescapingparameters(ClassSpecificationElement classSpecificationElement) + { + configuration.assumeNoEscapingParameters = extendClassSpecifications(configuration.assumeNoEscapingParameters, + classSpecificationElement); + } + + + public void addConfiguredAssumenoexternalreturnvalues(ClassSpecificationElement classSpecificationElement) + { + configuration.assumeNoExternalReturnValues = extendClassSpecifications(configuration.assumeNoExternalReturnValues, + classSpecificationElement); + } + + public void addConfiguredOptimizations(FilterElement filterElement) { addConfiguredOptimization(filterElement); diff --git a/src/proguard/ant/FilterElement.java b/ant/src/proguard/ant/FilterElement.java similarity index 97% rename from src/proguard/ant/FilterElement.java rename to ant/src/proguard/ant/FilterElement.java index b9323cf5e..f167ddee6 100644 --- a/src/proguard/ant/FilterElement.java +++ b/ant/src/proguard/ant/FilterElement.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/ant/KeepSpecificationElement.java b/ant/src/proguard/ant/KeepSpecificationElement.java similarity index 91% rename from src/proguard/ant/KeepSpecificationElement.java rename to ant/src/proguard/ant/KeepSpecificationElement.java index dec01361e..7771cb6c1 100644 --- a/src/proguard/ant/KeepSpecificationElement.java +++ b/ant/src/proguard/ant/KeepSpecificationElement.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -32,6 +32,7 @@ public class KeepSpecificationElement extends ClassSpecificationElement { private boolean markDescriptorClasses; + private boolean markCodeAttributes; private boolean allowShrinking; private boolean allowOptimization; private boolean allowObfuscation; @@ -58,9 +59,11 @@ public void appendTo(List keepSpecifications, new KeepClassSpecification(markClasses, markConditionally, markDescriptorClasses, + markCodeAttributes, allowShrinking, allowOptimization, allowObfuscation, + null, createClassSpecification(keepSpecificationElement)); // Add it to the list. @@ -76,6 +79,12 @@ public void setIncludedescriptorclasses(boolean markDescriptorClasses) } + public void setIncludecode(boolean markCodeAttributes) + { + this.markCodeAttributes = markCodeAttributes; + } + + public void setAllowshrinking(boolean allowShrinking) { this.allowShrinking = allowShrinking; diff --git a/src/proguard/ant/MemberSpecificationElement.java b/ant/src/proguard/ant/MemberSpecificationElement.java similarity index 99% rename from src/proguard/ant/MemberSpecificationElement.java rename to ant/src/proguard/ant/MemberSpecificationElement.java index 3eb712f60..6d7075bf2 100644 --- a/src/proguard/ant/MemberSpecificationElement.java +++ b/ant/src/proguard/ant/MemberSpecificationElement.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/ant/ProGuardTask.java b/ant/src/proguard/ant/ProGuardTask.java similarity index 87% rename from src/proguard/ant/ProGuardTask.java rename to ant/src/proguard/ant/ProGuardTask.java index 130ca6535..ce22a4b4b 100644 --- a/src/proguard/ant/ProGuardTask.java +++ b/ant/src/proguard/ant/ProGuardTask.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,6 +25,7 @@ import proguard.classfile.util.ClassUtil; import java.io.*; +import java.net.*; import java.util.*; /** @@ -45,8 +46,13 @@ public void setConfiguration(File configurationFile) throws BuildException Properties properties = new Properties(); properties.putAll(getProject().getProperties()); - ConfigurationParser parser = new ConfigurationParser(configurationFile, - properties); + URL configUrl = + ConfigurationElement.class.getResource(configurationFile.toString()); + + ConfigurationParser parser = configUrl != null ? + new ConfigurationParser(configUrl, properties) : + new ConfigurationParser(configurationFile, properties); + try { parser.parse(configuration); @@ -166,19 +172,19 @@ public void setApplymapping(File applyMapping) public void setObfuscationdictionary(File obfuscationDictionary) { - configuration.obfuscationDictionary = resolvedFile(obfuscationDictionary); + configuration.obfuscationDictionary = resolvedURL(obfuscationDictionary); } public void setClassobfuscationdictionary(File classObfuscationDictionary) { - configuration.classObfuscationDictionary = resolvedFile(classObfuscationDictionary); + configuration.classObfuscationDictionary = resolvedURL(classObfuscationDictionary); } public void setPackageobfuscationdictionary(File packageObfuscationDictionary) { - configuration.packageObfuscationDictionary = resolvedFile(packageObfuscationDictionary); + configuration.packageObfuscationDictionary = resolvedURL(packageObfuscationDictionary); } @@ -244,6 +250,12 @@ public void setMicroedition(boolean microEdition) } + public void setAndroid(boolean android) + { + configuration.android = android; + } + + public void setVerbose(boolean verbose) { configuration.verbose = verbose; @@ -306,6 +318,13 @@ public void setDump(File dump) } + public void setAddConfigurationDebugging(boolean addConfigurationDebugging) + { + configuration.addConfigurationDebugging = addConfigurationDebugging; + } + + + // Implementations for Task. public void execute() throws BuildException @@ -344,6 +363,23 @@ private File optionalFile(File file) } + /** + * Returns a URL that is properly resolved with respect to the project + * directory. + */ + private URL resolvedURL(File file) + { + try + { + return resolvedFile(file).toURI().toURL(); + } + catch (MalformedURLException e) + { + return null; + } + } + + /** * Returns a file that is properly resolved with respect to the project * directory. diff --git a/src/proguard/ant/package.html b/ant/src/proguard/ant/package.html similarity index 100% rename from src/proguard/ant/package.html rename to ant/src/proguard/ant/package.html diff --git a/src/proguard/ant/task.properties b/ant/src/proguard/ant/task.properties similarity index 100% rename from src/proguard/ant/task.properties rename to ant/src/proguard/ant/task.properties diff --git a/buildscripts/README b/buildscripts/README index 88c2591df..9d3431cff 100644 --- a/buildscripts/README +++ b/buildscripts/README @@ -3,40 +3,28 @@ 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.gradle : a Gradle build file for all platforms -- build.xml : an Ant build file for all platforms -- maven/pom.xml : a Maven POM for building the Maven artifacts +- build.sh: a shell script for GNU/Linux -- As a final alternative, you can also easily compile the code from the - command line: + ./build.sh - 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 +- makefile: a makefile for GNU/Linux - For the ProGuard Gradle task: + make clean all - javac -sourcepath src -d classes -classpath ..... \ - src/proguard/gradle/ProGuardTask.java +- build.gradle : a Gradle build file for all platforms - For the ProGuard Ant task: + gradle clean assemble - javac -sourcepath src -d classes -classpath lib/ant.jar \ - src/proguard/ant/ProGuardTask.java +- build.xml: an Ant build file for all platforms - For the Java Micro Edition Wireless Tool Kit (JME WTK) obfuscator plug-in: + ant clean all - javac -sourcepath src -d classes -classpath wtklib/kenv.zip \ - src/proguard/wtk/ProGuardObfuscator.java +- pom.xml: a Maven POM for building the Maven artifacts -Depending on the scripts, you may still need to install Gradle, Ant, Maven, -and the JME WTK yourself. + mvn clean package -Enjoy! +Pick your favorite build tool and enjoy! -http://proguard.sourceforge.net/ +https://www.guardsquare.com/proguard -Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare +Copyright (c) 2002-2018 Eric Lafortune @ GuardSquare diff --git a/buildscripts/build.gradle b/buildscripts/build.gradle index 229efb26a..2687e97a4 100644 --- a/buildscripts/build.gradle +++ b/buildscripts/build.gradle @@ -1,111 +1,64 @@ -apply plugin: 'java' - -defaultTasks 'proguardJar', 'retraceJar', 'proguardguiJar' - -sourceCompatibility = JavaVersion.VERSION_1_6 -targetCompatibility = JavaVersion.VERSION_1_6 - -sourceSets { - proguard { - java { - srcDirs = ['../src'] - exclude 'proguard/gui/**' - exclude 'proguard/retrace/**' - exclude 'proguard/ant/**' - exclude 'proguard/gradle/**' - exclude 'proguard/wtk/**' - } - } +// Gradle build script for all ProGuard jars. - retrace { - java { - srcDirs = ['../src'] - include 'proguard/retrace/**' - } - } +task clean { + delete fileTree('../lib') +} - anttask { - java { - srcDirs = ['../src'] - include 'proguard/ant/**' - } - resources { - srcDirs = ['../src'] - include 'proguard/ant/**.properties' - } - } +// Collect the main ProGuard jar. - gradletask { - java { - srcDirs = ['../src'] - include 'proguard/gradle/**' - } - } +task assembleProguardJar(type: Jar) { + destinationDir = file('../lib') + baseName = 'proguard' - proguardgui { - java { - srcDirs = ['../src'] - include 'proguard/gui/**' - } - resources { - srcDirs = ['../src'] - include 'proguard/gui/**.properties' - include 'proguard/gui/**.pro' - include 'proguard/gui/**.png' - include 'proguard/gui/**.gif' - } - } + manifest.from '../core/src/META-INF/MANIFEST.MF' } -repositories { - mavenCentral() +def proguardSubprojects = + [':core', ':gradle', ':ant', ':wtk'].collect{ project(it) } + +proguardSubprojects.each { subproject -> + subproject.afterEvaluate { + assembleProguardJar.dependsOn subproject.tasks['jar'] + assembleProguardJar.from subproject.configurations.archives.artifacts.files.collect { zipTree(it) } + } } -dependencies { - retraceCompile sourceSets.proguard.output - anttaskCompile sourceSets.proguard.output - anttaskCompile 'org.apache.ant:ant:1.7.0' - gradletaskCompile sourceSets.proguard.output - gradletaskCompile gradleApi() - gradletaskCompile localGroovy() - proguardguiCompile sourceSets.proguard.output - proguardguiCompile sourceSets.retrace.output +// Copy the ReTrace jar. + +task copyRetraceJar(type: Copy) { + into '../lib' } -task proguardJar(type: Jar) { - from sourceSets.proguard.output - from sourceSets.anttask.output - from sourceSets.gradletask.output - destinationDir = file('../lib') - baseName = 'proguard' +project(':retrace').afterEvaluate { + copyRetraceJar.from it.tasks['jar'] +} - manifest.from '../src/proguard/MANIFEST.MF' +// Copy the GUI jar. - // Delete the original jar first, otherwise the - // jar task will not overwrite the file. - file('../lib/proguard.jar').delete() +task copyGuiJar(type: Copy) { + into '../lib' + rename 'gui.jar', 'proguardgui.jar' } -task retraceJar(type: Jar) { - from sourceSets.retrace.output - destinationDir = file('../lib') - baseName = 'retrace' +project(':gui').afterEvaluate { + copyGuiJar.from it.tasks['jar'] +} - manifest.from '../src/proguard/retrace/MANIFEST.MF' +// Copy the annotations jar. - // Delete the original jar first, otherwise the - // jar task will not overwrite the file. - file('../lib/retrace.jar').delete() +task copyAnnotationsJar(type: Copy) { + into '../lib' } -task proguardguiJar(type: Jar) { - from sourceSets.proguardgui.output - destinationDir = file('../lib') - baseName = 'proguardgui' +project(':annotations').afterEvaluate { + copyAnnotationsJar.from it.tasks['jar'] +} - manifest.from '../src/proguard/gui/MANIFEST.MF' +// Assemble or copy all jars. - // Delete the original jar first, otherwise the - // jar task will not overwrite the file. - file('../lib/proguardgui.jar').delete() +task assemble { + dependsOn assembleProguardJar + dependsOn copyRetraceJar + dependsOn copyGuiJar + dependsOn copyAnnotationsJar } diff --git a/buildscripts/build.properties b/buildscripts/build.properties deleted file mode 100644 index a6d2bbf5b..000000000 --- a/buildscripts/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -# Ant build properties for ProGuard. - -gradle.home = /usr/local/java/gradle -wtk.home = /usr/local/java/wtk diff --git a/buildscripts/build.sh b/buildscripts/build.sh index ef5cf0b29..16c8c1c90 100755 --- a/buildscripts/build.sh +++ b/buildscripts/build.sh @@ -2,111 +2,15 @@ # # 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-*.jar:\ -$GRADLE_HOME/lib/gradle-logging-*.jar:\ -$GRADLE_HOME/lib/gradle-base-services-*.jar:\ -$GRADLE_HOME/lib/gradle-base-services-groovy-*.jar:\ -$GRADLE_HOME/lib/gradle-core-*.jar:\ -$GRADLE_HOME/lib/groovy-all-*.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 +cd $(dirname "$0") + +# Standard modules. +../core/build.sh && \ +../retrace/build.sh && \ +../gui/build.sh && \ +../annotations/build.sh || exit 1 + +# Optional modules. +../gradle/build.sh +../ant/build.sh +../wtk/build.sh diff --git a/buildscripts/build.xml b/buildscripts/build.xml index 370a76162..154865dfd 100644 --- a/buildscripts/build.xml +++ b/buildscripts/build.xml @@ -4,207 +4,42 @@ default = "all" basedir = ".."> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - + - + + - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - - - - - diff --git a/buildscripts/functions.sh b/buildscripts/functions.sh new file mode 100755 index 000000000..90be905bc --- /dev/null +++ b/buildscripts/functions.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Support functions for building ProGuard. + +SRC=src +OUT=out +LIB=../lib + +PROGUARD_JAR=$LIB/proguard.jar +RETRACE_JAR=$LIB/retrace.jar +PROGUARD_GUI_JAR=$LIB/proguardgui.jar +ANNOTATIONS_JAR=$LIB/annotations.jar + +set -o pipefail + +function compile { + # Compile java source files. + echo "Compiling $(basename $PWD) ($1)..." + mkdir -p "$OUT" && \ + javac -nowarn -Xlint:none -sourcepath "$SRC" -d "$OUT" \ + "$SRC"/${1//.//}.java 2>&1 \ + | sed -e 's|^| |' || return 1 + + # Copy resource files. + (cd "$SRC" && \ + find proguard \ + \( -name \*.properties -o -name \*.png -o -name \*.gif -o -name \*.pro \) \ + -exec cp --parents {} "../$OUT" \; ) +} + +function createjar { + echo "Creating $1..." + if [ -f "$SRC/META-INF/MANIFEST.MF" ]; then + jar -cfm "$1" "$SRC/META-INF/MANIFEST.MF" -C "$OUT" proguard + else + jar -cf "$1" -C "$OUT" proguard + fi +} + +function updatejar { + echo "Updating $1..." + jar -uf "$1" -C "$OUT" proguard +} diff --git a/buildscripts/gradle.properties b/buildscripts/gradle.properties new file mode 100644 index 000000000..cc20def13 --- /dev/null +++ b/buildscripts/gradle.properties @@ -0,0 +1 @@ +wtkDir=/usr/local/java/wtk2.1 diff --git a/buildscripts/makefile b/buildscripts/makefile index 4518a5342..c6a580041 100644 --- a/buildscripts/makefile +++ b/buildscripts/makefile @@ -1,109 +1,22 @@ # GNU/Linux makefile for ProGuard. -ANT_HOME = /usr/local/java/ant -GRADLE_HOME = /usr/local/java/gradle -WTK_HOME = /usr/local/java/wtk +BASIC_MODULES = core retrace gui annotations +OPTIONAL_MODULES = gradle ant wtk -PROGUARD_HOME := $(subst ./..,..,$(subst /buildscripts/..,/,$(dir $(MAKEFILE_LIST))..)) -SRC = $(PROGUARD_HOME)/src -CLASSES = $(PROGUARD_HOME)/classes -LIB = $(PROGUARD_HOME)/lib +MODULES = $(BASIC_MODULES) $(OPTIONAL_MODULES) -ANT_JAR = $(ANT_HOME)/lib/ant.jar -GRADLE_JARS = $(wildcard \ - $(GRADLE_HOME)/lib/plugins/gradle-plugins-*.jar \ - $(GRADLE_HOME)/lib/gradle-logging-*.jar \ - $(GRADLE_HOME)/lib/gradle-base-services-*.jar \ - $(GRADLE_HOME)/lib/gradle-base-services-groovy-*.jar \ - $(GRADLE_HOME)/lib/gradle-core-*.jar \ - $(GRADLE_HOME)/lib/groovy-all-*.jar) -WTK_JAR = $(WTK_HOME)/wtklib/kenv.zip +LIB = ../lib -NOTHING:= -SPACE:=$(NOTHING) $(NOTHING) -CLASSPATH = $(ANT_JAR):$(subst $(SPACE),:,$(GRADLE_JARS)):$(WTK_JAR) +# The main targets. -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 +all: basic optional +basic: $(BASIC_MODULES) +optional: $(OPTIONAL_MODULES) -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) +$(MODULES): + cd ../$@ && $(MAKE) 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." + rm -fr $(LIB) -.PHONY: all basic options proguard proguardgui retrace anttask wtkplugin clean $(TARGETS) $(OPTIONAL_TARGETS) +.PHONY: all basic optional $(MODULES) clean diff --git a/buildscripts/maven/base/pom.xml b/buildscripts/maven/base/pom.xml deleted file mode 100644 index c18a0edc7..000000000 --- a/buildscripts/maven/base/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - 4.0.0 - - net.sf.proguard - proguard-parent - 5.3.3 - ../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/**.java - - - 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/buildscripts/maven/pom.xml b/buildscripts/pom.xml similarity index 92% rename from buildscripts/maven/pom.xml rename to buildscripts/pom.xml index 58e6a729d..6d1bf2ada 100644 --- a/buildscripts/maven/pom.xml +++ b/buildscripts/pom.xml @@ -7,7 +7,7 @@ net.sf.proguard proguard-parent - 5.3.3 + 6.0 pom [${project.groupId}] ${project.artifactId} ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. @@ -137,16 +137,13 @@ - - - base - gui - ant - gradle - retrace + ../core + ../retrace + ../gui + ../gradle + ../ant + ../annotations @@ -158,7 +155,7 @@ - wtk + ../wtk diff --git a/buildscripts/settings.gradle b/buildscripts/settings.gradle new file mode 100644 index 000000000..f3185ec73 --- /dev/null +++ b/buildscripts/settings.gradle @@ -0,0 +1,12 @@ +includeFlat 'core' +includeFlat 'retrace' +includeFlat 'gui' +includeFlat 'gradle' +includeFlat 'ant' +includeFlat 'annotations' + +if (file(wtkDir).isDirectory()) { + includeFlat 'wtk' +} else { + System.err.println "Can't find the WTK directory [${wtkDir}]. Not building the WTK plugin." +} diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 000000000..b6e6a9fbd --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,20 @@ +// Gradle build script for ProGuard. + +apply plugin: 'java' + +sourceSets.main { + java { + srcDirs = ['src'] + } + resources { + srcDirs = ['src'] + include '**/*.properties' + include '**/*.gif' + include '**/*.png' + include '**/*.pro' + } +} + +jar { + manifest.from 'src/META-INF/MANIFEST.MF' +} diff --git a/core/build.sh b/core/build.sh new file mode 100755 index 000000000..7ba492d35 --- /dev/null +++ b/core/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# GNU/Linux build script for ProGuard. + +cd $(dirname "$0") + +source ../buildscripts/functions.sh + +MAIN_CLASS=proguard.ProGuard + +compile $MAIN_CLASS && \ +createjar "$PROGUARD_JAR" || exit 1 diff --git a/core/build.xml b/core/build.xml new file mode 100644 index 000000000..1f8613042 --- /dev/null +++ b/core/build.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/makefile b/core/makefile new file mode 100644 index 000000000..e982b47b9 --- /dev/null +++ b/core/makefile @@ -0,0 +1,8 @@ +# GNU/Linux makefile for ProGuard. + +MAIN_CLASS = proguard/ProGuard +CLASSPATH = +TARGET = proguard +INCLUDE_MANIFEST = true + +include ../buildscripts/functions.mk diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 000000000..56d7b75b9 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,41 @@ + + + + 4.0.0 + + net.sf.proguard + proguard-parent + 6.0 + ../buildscripts/pom.xml + + proguard-base + [${project.groupId}] ${project.artifactId} + + + src + + + maven-source-plugin + + + maven-compiler-plugin + + + maven-jar-plugin + + + + proguard.ProGuard + + + + + + maven-javadoc-plugin + + + + diff --git a/src/proguard/MANIFEST.MF b/core/src/META-INF/MANIFEST.MF similarity index 100% rename from src/proguard/MANIFEST.MF rename to core/src/META-INF/MANIFEST.MF diff --git a/src/proguard/ArgumentWordReader.java b/core/src/proguard/ArgumentWordReader.java similarity index 95% rename from src/proguard/ArgumentWordReader.java rename to core/src/proguard/ArgumentWordReader.java index 172213a3a..227f1765b 100644 --- a/src/proguard/ArgumentWordReader.java +++ b/core/src/proguard/ArgumentWordReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -87,7 +87,7 @@ public static void main(String[] args) { { while (true) { - String word = reader.nextWord(false); + String word = reader.nextWord(false, false); if (word == null) System.exit(-1); diff --git a/src/proguard/AssumeNoSideEffectsChecker.java b/core/src/proguard/AssumeNoSideEffectsChecker.java similarity index 98% rename from src/proguard/AssumeNoSideEffectsChecker.java rename to core/src/proguard/AssumeNoSideEffectsChecker.java index 890bc6677..c0034395c 100644 --- a/src/proguard/AssumeNoSideEffectsChecker.java +++ b/core/src/proguard/AssumeNoSideEffectsChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/ClassMemberChecker.java b/core/src/proguard/ClassMemberChecker.java similarity index 99% rename from src/proguard/ClassMemberChecker.java rename to core/src/proguard/ClassMemberChecker.java index 2cf95034b..b0161b8d4 100644 --- a/src/proguard/ClassMemberChecker.java +++ b/core/src/proguard/ClassMemberChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/ClassPath.java b/core/src/proguard/ClassPath.java similarity index 97% rename from src/proguard/ClassPath.java rename to core/src/proguard/ClassPath.java index 7001a0d91..b4b4f6f8b 100644 --- a/src/proguard/ClassPath.java +++ b/core/src/proguard/ClassPath.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/ClassPathEntry.java b/core/src/proguard/ClassPathEntry.java similarity index 80% rename from src/proguard/ClassPathEntry.java rename to core/src/proguard/ClassPathEntry.java index c1e0fd470..11cf3ca02 100644 --- a/src/proguard/ClassPathEntry.java +++ b/core/src/proguard/ClassPathEntry.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -45,6 +45,7 @@ public class ClassPathEntry private List aarFilter; private List warFilter; private List earFilter; + private List jmodFilter; private List zipFilter; private String cachedName; @@ -182,6 +183,15 @@ public boolean isEar() } + /** + * Returns whether this data entry is a jmod file. + */ + public boolean isJmod() + { + return hasExtension(".jmod"); + } + + /** * Returns whether this data entry is a zip file. */ @@ -219,13 +229,14 @@ private static boolean endsWithIgnoreCase(String string, String suffix) */ public boolean isFiltered() { - return filter != null || - apkFilter != null || - jarFilter != null || - aarFilter != null || - warFilter != null || - earFilter != null || - zipFilter != null; + return filter != null || + apkFilter != null || + jarFilter != null || + aarFilter != null || + warFilter != null || + earFilter != null || + jmodFilter != null || + zipFilter != null; } @@ -331,6 +342,22 @@ public void setEarFilter(List filter) } + /** + * Returns the name filter that is applied to jmod files in this entry, if any. + */ + public List getJmodFilter() + { + return jmodFilter; + } + + /** + * Sets the name filter that is applied to jmod files in this entry, if any. + */ + public void setJmodFilter(List filter) + { + this.jmodFilter = filter == null || filter.size() == 0 ? null : jmodFilter; + } + /** * Returns the name filter that is applied to zip files in this entry, if any. */ @@ -354,28 +381,31 @@ public String toString() { String string = getName(); - if (filter != null || - jarFilter != null || - aarFilter != null || - warFilter != null || - earFilter != null || - zipFilter != null) + if (filter != null || + jarFilter != null || + aarFilter != null || + warFilter != null || + earFilter != null || + jmodFilter != null || + zipFilter != null) { string += ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + - (aarFilter != null ? ListUtil.commaSeparatedString(aarFilter, true) : "") + + (aarFilter != null ? ListUtil.commaSeparatedString(aarFilter, true) : "") + + ConfigurationConstants.SEPARATOR_KEYWORD + + (apkFilter != null ? ListUtil.commaSeparatedString(apkFilter, true) : "") + ConfigurationConstants.SEPARATOR_KEYWORD + - (apkFilter != null ? ListUtil.commaSeparatedString(apkFilter, true) : "") + + (zipFilter != null ? ListUtil.commaSeparatedString(zipFilter, true) : "") + ConfigurationConstants.SEPARATOR_KEYWORD + - (zipFilter != null ? ListUtil.commaSeparatedString(zipFilter, true) : "") + + (jmodFilter != null ? ListUtil.commaSeparatedString(jmodFilter, true) : "") + ConfigurationConstants.SEPARATOR_KEYWORD + - (earFilter != null ? ListUtil.commaSeparatedString(earFilter, true) : "") + + (earFilter != null ? ListUtil.commaSeparatedString(earFilter, true) : "") + ConfigurationConstants.SEPARATOR_KEYWORD + - (warFilter != null ? ListUtil.commaSeparatedString(warFilter, true) : "") + + (warFilter != null ? ListUtil.commaSeparatedString(warFilter, true) : "") + ConfigurationConstants.SEPARATOR_KEYWORD + - (jarFilter != null ? ListUtil.commaSeparatedString(jarFilter, true) : "") + + (jarFilter != null ? ListUtil.commaSeparatedString(jarFilter, true) : "") + ConfigurationConstants.SEPARATOR_KEYWORD + - (filter != null ? ListUtil.commaSeparatedString(filter, true) : "") + + (filter != null ? ListUtil.commaSeparatedString(filter, true) : "") + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD; } diff --git a/src/proguard/ClassSpecification.java b/core/src/proguard/ClassSpecification.java similarity index 99% rename from src/proguard/ClassSpecification.java rename to core/src/proguard/ClassSpecification.java index efdeb1c81..43d4cb21f 100644 --- a/src/proguard/ClassSpecification.java +++ b/core/src/proguard/ClassSpecification.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -40,6 +40,7 @@ public class ClassSpecification implements Cloneable public final String extendsAnnotationType; public final String extendsClassName; + public final List attributeNames = null; public List fieldSpecifications; public List methodSpecifications; diff --git a/core/src/proguard/ClassSpecificationVisitorFactory.java b/core/src/proguard/ClassSpecificationVisitorFactory.java new file mode 100644 index 000000000..94d696fba --- /dev/null +++ b/core/src/proguard/ClassSpecificationVisitorFactory.java @@ -0,0 +1,674 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; +import proguard.classfile.visitor.*; +import proguard.util.*; + +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, class members, and attributes. + * + * @param classSpecifications the list of ClassSpecification instances + * that specify the classes and class members + * to visit. + * @param classVisitor an optional ClassVisitor to be applied to + * all classes. + * @param memberVisitor an optional MemberVisitor to be applied to + * matching fields and methods. + */ + public ClassPoolVisitor createClassPoolVisitor(List classSpecifications, + ClassVisitor classVisitor, + MemberVisitor memberVisitor) + { + return createClassPoolVisitor(classSpecifications, + classVisitor, + memberVisitor, + memberVisitor, + null); + } + + + /** + * Constructs a ClassPoolVisitor to efficiently travel to the specified + * classes and class members. + * + * @param classSpecifications the list of ClassSpecification instances + * that specify the classes and class members + * to visit. + * @param classVisitor an optional ClassVisitor to be applied to + * all classes. + * @param fieldVisitor an optional MemberVisitor to be applied to + * matching fields. + * @param methodVisitor an optional MemberVisitor to be applied to + * matching methods. + * @param attributeVisitor an optional AttributeVisitor to be applied + * to matching attributes. + */ + public ClassPoolVisitor createClassPoolVisitor(List classSpecifications, + ClassVisitor classVisitor, + MemberVisitor fieldVisitor, + MemberVisitor methodVisitor, + AttributeVisitor attributeVisitor) + { + 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, + fieldVisitor, + methodVisitor, + attributeVisitor, + null)); + } + } + + return multiClassPoolVisitor; + } + + + /** + * Constructs a ClassPoolVisitor to efficiently travel to the specified + * classes, class members, and attributes. + * + * @param classSpecification the specifications of the class(es) and class + * members to visit. + * @param classVisitor an optional ClassVisitor to be applied to + * matching classes. + * @param fieldVisitor an optional MemberVisitor to be applied to + * matching fields. + * @param methodVisitor an optional MemberVisitor to be applied to + * matching methods. + * @param attributeVisitor an optional AttributeVisitor to be applied + * to matching attributes. + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + */ + protected ClassPoolVisitor createClassPoolVisitor(ClassSpecification classSpecification, + ClassVisitor classVisitor, + MemberVisitor fieldVisitor, + MemberVisitor methodVisitor, + AttributeVisitor attributeVisitor, + List variableStringMatchers) + { + String className = classSpecification.className; + String annotationType = classSpecification.annotationType; + String extendsAnnotationType = classSpecification.extendsAnnotationType; + String extendsClassName = classSpecification.extendsClassName; + + // We explicitly need to match a wildcard class name, so it can be + // referenced through its variable string matcher. + if (className == null) + { + className = "**"; + } + + // We need to parse the class names before any class member names, to + // make sure the list of variable string matchers is filled out in the + // right order. + StringMatcher annotationTypeMatcher = annotationType == null ? null : + new ListParser(new ClassNameParser(variableStringMatchers)).parse(annotationType); + + StringMatcher classNameMatcher = + new ListParser(new ClassNameParser(variableStringMatchers)).parse(className); + + StringMatcher extendsAnnotationTypeMatcher = extendsAnnotationType == null ? null : + new ListParser(new ClassNameParser(variableStringMatchers)).parse(extendsAnnotationType); + + StringMatcher extendsClassNameMatcher = extendsClassName == null ? null : + new ListParser(new ClassNameParser(variableStringMatchers)).parse(extendsClassName); + + // Combine both visitors. + ClassVisitor combinedClassVisitor = + createCombinedClassVisitor(classSpecification.attributeNames, + classSpecification.fieldSpecifications, + classSpecification.methodSpecifications, + classVisitor, + fieldVisitor, + methodVisitor, + attributeVisitor, + variableStringMatchers); + + // If the class name has wildcards, only visit classes with matching names. + if (extendsAnnotationType != null || + extendsClassName != null || + containsWildCards(className)) + { + combinedClassVisitor = + new ClassNameFilter(classNameMatcher, combinedClassVisitor); + + // We'll have to visit all classes now. + className = null; + } + + // If specified, only visit classes with the right annotation. + if (annotationType != null) + { + combinedClassVisitor = + new AllAttributeVisitor( + new AllAnnotationVisitor( + new AnnotationTypeFilter(annotationTypeMatcher, + new AnnotationToAnnotatedClassVisitor(combinedClassVisitor)))); + } + + // If specified, only visit classes with the right access flags. + if (classSpecification.requiredSetAccessFlags != 0 || + classSpecification.requiredUnsetAccessFlags != 0) + { + combinedClassVisitor = + new ClassAccessFilter(classSpecification.requiredSetAccessFlags, + classSpecification.requiredUnsetAccessFlags, + combinedClassVisitor); + } + + // If it's specified, start visiting from the extended class. + if (extendsAnnotationType != null || + extendsClassName != null) + { + // Start visiting from the extended class. + combinedClassVisitor = + new ClassHierarchyTraveler(false, false, false, true, + combinedClassVisitor); + + // If specified, only visit extended classes with the right annotation. + if (extendsAnnotationType != null) + { + combinedClassVisitor = + new AllAttributeVisitor( + new AllAnnotationVisitor( + new AnnotationTypeFilter(extendsAnnotationTypeMatcher, + new AnnotationToAnnotatedClassVisitor(combinedClassVisitor)))); + } + + // If specified, only visit extended classes with matching names. + if (extendsClassName != null) + { + // If wildcarded, only visit extended classes with matching names. + if (containsWildCards(extendsClassName)) + { + combinedClassVisitor = + new ClassNameFilter(extendsClassNameMatcher, + combinedClassVisitor); + } + else + { + // Start visiting from the named extended class. + className = extendsClassName; + } + } + } + + // If specified, visit a single named class, otherwise visit all classes. + return className != null ? + new NamedClassVisitor(combinedClassVisitor, className) : + new AllClassVisitor(combinedClassVisitor); + } + + + /** + * Constructs a ClassVisitor to efficiently delegate to the given ClassVisitor + * and travel to the specified class members and attributes. + * @param attributeNames optional names (with wildcards) of class + * attributes to visit. + * @param fieldSpecifications optional specifications of the fields to + * visit. + * @param methodSpecifications optional specifications of the methods to + * visit. + * @param classVisitor an optional ClassVisitor to be applied to + * all classes. + * @param fieldVisitor an optional MemberVisitor to be applied to + * matching fields. + * @param methodVisitor an optional MemberVisitor to be applied to + * matching methods. + * @param attributeVisitor an optional AttributeVisitor to be applied + * to matching attributes. + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + */ + protected ClassVisitor createCombinedClassVisitor(List attributeNames, + List fieldSpecifications, + List methodSpecifications, + ClassVisitor classVisitor, + MemberVisitor fieldVisitor, + MemberVisitor methodVisitor, + AttributeVisitor attributeVisitor, + List variableStringMatchers) + { + // Don't visit any members if there aren't any member specifications. + if (fieldSpecifications == null) + { + fieldVisitor = null; + } + + if (methodSpecifications == null) + { + methodVisitor = 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 (fieldVisitor == null && + methodVisitor == null && + attributeVisitor == null) + { + return classVisitor; + } + + multiClassVisitor.addClassVisitor(classVisitor); + } + + // If specified, let the attribute visitor visit the class attributes. + if (attributeVisitor != null) + { + // If specified, only visit attributes with the right names. + if (attributeNames != null) + { + attributeVisitor = + new AttributeNameFilter(attributeNames, attributeVisitor); + } + + multiClassVisitor.addClassVisitor(new AllAttributeVisitor(attributeVisitor)); + } + + // If specified, let the member info visitor visit the class members. + if (fieldVisitor != null || + methodVisitor != null) + { + ClassVisitor memberClassVisitor = + createClassVisitor(fieldSpecifications, + methodSpecifications, + fieldVisitor, + methodVisitor, + attributeVisitor, + variableStringMatchers); + + // 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 fieldSpecifications the specifications of the fields to visit. + * @param methodSpecifications the specifications of the methods to visit. + * @param fieldVisitor an optional MemberVisitor to be applied to + * matching fields. + * @param methodVisitor an optional MemberVisitor to be applied to + * matching methods. + * @param attributeVisitor an optional AttributeVisitor to be applied + * to matching attributes. + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + */ + private ClassVisitor createClassVisitor(List fieldSpecifications, + List methodSpecifications, + MemberVisitor fieldVisitor, + MemberVisitor methodVisitor, + AttributeVisitor attributeVisitor, + List variableStringMatchers) + { + MultiClassVisitor multiClassVisitor = new MultiClassVisitor(); + + addMemberVisitors(fieldSpecifications, true, multiClassVisitor, fieldVisitor, attributeVisitor, variableStringMatchers); + addMemberVisitors(methodSpecifications, false, multiClassVisitor, methodVisitor, attributeVisitor, variableStringMatchers); + + // 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 void addMemberVisitors(List memberSpecifications, + boolean isField, + MultiClassVisitor multiClassVisitor, + MemberVisitor memberVisitor, + AttributeVisitor attributeVisitor, + List variableStringMatchers) + { + if (memberSpecifications != null) + { + for (int index = 0; index < memberSpecifications.size(); index++) + { + MemberSpecification memberSpecification = + (MemberSpecification)memberSpecifications.get(index); + + multiClassVisitor.addClassVisitor( + createClassVisitor(memberSpecification, + isField, + memberVisitor, + attributeVisitor, + variableStringMatchers)); + } + } + } + + + /** + * Constructs a ClassPoolVisitor that conditionally applies the given + * ClassPoolVisitor for all classes that match the given class + * specification. + */ + protected ClassPoolVisitor createClassTester(ClassSpecification classSpecification, + ClassPoolVisitor classPoolVisitor, + List variableStringMatchers) + { + ClassPoolClassVisitor classPoolClassVisitor = + new ClassPoolClassVisitor(classPoolVisitor); + + // Parse the class condition. + ClassPoolVisitor conditionalClassTester = + createClassTester(classSpecification, + (ClassVisitor)classPoolClassVisitor, + variableStringMatchers); + + // The ClassPoolClassVisitor first needs to visit the class pool + // and then its classes. + return new MultiClassPoolVisitor( + new ClassPoolVisitor[] + { + classPoolClassVisitor, + conditionalClassTester + }); + } + + + /** + * Constructs a ClassPoolVisitor that conditionally applies the given + * ClassVisitor to all classes that match the given class specification. + */ + protected ClassPoolVisitor createClassTester(ClassSpecification classSpecification, + ClassVisitor classVisitor, + List variableStringMatchers) + { + // Create a placeholder for the class visitor that tests class + // members. + MultiClassVisitor conditionalMemberTester = + new MultiClassVisitor(); + + // Parse the class condition. + ClassPoolVisitor conditionalClassTester = + createClassPoolVisitor(classSpecification, + conditionalMemberTester, + null, + null, + null, + variableStringMatchers); + + // Parse the member conditions and add the result to the placeholder. + conditionalMemberTester.addClassVisitor( + createClassMemberTester(classSpecification.fieldSpecifications, + classSpecification.methodSpecifications, + classVisitor, + variableStringMatchers)); + + return conditionalClassTester; + } + + + /** + * Constructs a ClassVisitor that conditionally applies the given + * ClassVisitor to all classes that contain the given class members. + */ + private ClassVisitor createClassMemberTester(List fieldSpecifications, + List methodSpecifications, + ClassVisitor classVisitor, + List variableStringMatchers) + { + // Create a linked list of conditional visitors, for fields and for + // methods. + return createClassMemberTester(fieldSpecifications, + true, + createClassMemberTester(methodSpecifications, + false, + classVisitor, variableStringMatchers), + variableStringMatchers); + } + + + /** + * 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 ClassVisitor createClassMemberTester(List memberSpecifications, + boolean isField, + ClassVisitor classVisitor, + List variableStringMatchers) + { + // 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), + null, + variableStringMatchers); + } + } + + return classVisitor; + } + + + /** + * Creates a new ClassVisitor to efficiently travel to the specified class + * members and attributes. + * + * @param memberSpecification the specification of the class member(s) to + * visit. + * @param memberVisitor the MemberVisitor to be applied to matching + * class member(s). + * @param variableStringMatchers a mutable list of VariableStringMatcher + * instances that match the wildcards. + */ + private ClassVisitor createClassVisitor(MemberSpecification memberSpecification, + boolean isField, + MemberVisitor memberVisitor, + AttributeVisitor attributeVisitor, + List variableStringMatchers) + { + String annotationType = memberSpecification.annotationType; + String name = memberSpecification.name; + String descriptor = memberSpecification.descriptor; + List attributeNames = memberSpecification.attributeNames; + + // We need to parse the names before the descriptors, to make sure the + // list of variable string matchers is filled out in the right order. + StringMatcher annotationTypeMatcher = annotationType == null ? null : + new ListParser(new ClassNameParser(variableStringMatchers)).parse(annotationType); + + StringMatcher nameMatcher = name == null ? null : + new ListParser(new NameParser(variableStringMatchers)).parse(name); + + StringMatcher descriptorMatcher = name == null ? null : + new ListParser(new ClassNameParser(variableStringMatchers)).parse(descriptor); + + StringMatcher attributesMatcher = attributeNames == null ? null : + new ListParser(new NameParser(variableStringMatchers)).parse(attributeNames); + + // If specified, let the attribute visitor visit the class member + // attributes. + if (attributeVisitor != null) + { + // If specified, only visit attributes with the right names. + if (attributesMatcher != null) + { + attributeVisitor = + new AttributeNameFilter(attributesMatcher, attributeVisitor); + } + + memberVisitor = + new MultiMemberVisitor( + new MemberVisitor[] + { + memberVisitor, + new AllAttributeVisitor(attributeVisitor) + }); + } + + // If specified, only visit class members with the right annotation. + if (memberSpecification.annotationType != null) + { + memberVisitor = + new AllAttributeVisitor( + new AllAnnotationVisitor( + new AnnotationTypeFilter(annotationTypeMatcher, + new AnnotationToAnnotatedMemberVisitor(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); + } + + // Are the name and descriptor fully specified? + if (name != null && + descriptor != null && + !containsWildCards(name) && + !containsWildCards(descriptor)) + { + // Somewhat more efficiently, visit a single named class member. + return isField ? + new NamedFieldVisitor(name, descriptor, memberVisitor) : + new NamedMethodVisitor(name, descriptor, memberVisitor); + } + + // If specified, only visit class members with the right descriptors. + if (descriptorMatcher != null) + { + memberVisitor = + new MemberDescriptorFilter(descriptorMatcher, memberVisitor); + } + + // If specified, only visit class members with the right names. + if (name != null) + { + memberVisitor = + new MemberNameFilter(nameMatcher, memberVisitor); + } + + // Visit all class members, filtering the matching ones. + return isField ? + new AllFieldVisitor(memberVisitor) : + new AllMethodVisitor(memberVisitor); + } + + + // Small utility methods. + + /** + * Returns whether the given string contains a wild card. + */ + private 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 || + containsWildCardReferences(string)); + } + + + /** + * Returns whether the given string contains a numeric reference to a + * wild card (""). + */ + private boolean containsWildCardReferences(String string) + { + int openIndex = string.indexOf('<'); + if (openIndex < 0) + { + return false; + } + + int closeIndex = string.indexOf('>', openIndex + 1); + if (closeIndex < 0) + { + return false; + } + + try + { + Integer.parseInt(string.substring(openIndex + 1, closeIndex)); + } + catch (NumberFormatException e) + { + return false; + } + + return true; + } +} diff --git a/src/proguard/Configuration.java b/core/src/proguard/Configuration.java similarity index 87% rename from src/proguard/Configuration.java rename to core/src/proguard/Configuration.java index 0df9884a9..36cbeec19 100644 --- a/src/proguard/Configuration.java +++ b/core/src/proguard/Configuration.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,7 +21,8 @@ package proguard; import java.io.File; -import java.util.List; +import java.net.URL; +import java.util.*; /** * The ProGuard configuration. @@ -40,12 +41,12 @@ public class Configuration /////////////////////////////////////////////////////////////////////////// /** - * A list of input and output entries (jars, wars, ears, zips, and directories). + * A list of input and output entries (jars, wars, ears, jmods, zips, and directories). */ public ClassPath programJars; /** - * A list of library entries (jars, wars, ears, zips, and directories). + * A list of library entries (jars, wars, ears, jmods, zips, and directories). */ public ClassPath libraryJars; @@ -151,6 +152,25 @@ public class Configuration */ public List assumeNoSideEffects; + /** + * A list of {@link ClassSpecification} instances, whose methods are + * assumed to have no side external effects (that is, outside of 'this'). + */ + public List assumeNoExternalSideEffects; + + /** + * A list of {@link ClassSpecification} instances, whose methods are + * assumed not to let any reference parameters escape (including 'this'). + */ + public List assumeNoEscapingParameters; + + /** + * A list of {@link ClassSpecification} instances, whose methods are + * assumed not to return any external references (only parameters and new + * instances). + */ + public List assumeNoExternalReturnValues; + /** * Specifies whether the access of class members can be modified. */ @@ -184,17 +204,17 @@ public class Configuration /** * An optional name of a file containing obfuscated class member names. */ - public File obfuscationDictionary; + public URL obfuscationDictionary; /** * An optional name of a file containing obfuscated class names. */ - public File classObfuscationDictionary; + public URL classObfuscationDictionary; /** * An optional name of a file containing obfuscated package names. */ - public File packageObfuscationDictionary; + public URL packageObfuscationDictionary; /** * Specifies whether to apply aggressive name overloading on class members. @@ -286,6 +306,11 @@ public class Configuration */ public boolean microEdition = false; + /** + * Specifies whether the code should be targeted at the Android platform. + */ + public boolean android = false; + /////////////////////////////////////////////////////////////////////////// // General options. /////////////////////////////////////////////////////////////////////////// @@ -328,4 +353,17 @@ public class Configuration * or less readable form. An empty file name means the standard output. */ public File dump; + + /** + * Specifies whether to add logging to reflection code, providing suggestions + * on the ProGuard configuration. + */ + public boolean addConfigurationDebugging; + + /** + * Specifies whether to backporting of class files to another + * targetClassVersion shall be enabled. + */ + public boolean backport = false; + } diff --git a/src/proguard/ConfigurationChecker.java b/core/src/proguard/ConfigurationChecker.java similarity index 96% rename from src/proguard/ConfigurationChecker.java rename to core/src/proguard/ConfigurationChecker.java index 63c0f6586..60d769979 100644 --- a/src/proguard/ConfigurationChecker.java +++ b/core/src/proguard/ConfigurationChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -103,11 +103,12 @@ public void check() throws IOException // Is it an output directory? ClassPathEntry entry = programJars.get(index); if (entry.isOutput() && - !entry.isApk() && - !entry.isJar() && - !entry.isAar() && - !entry.isWar() && - !entry.isEar() && + !entry.isApk() && + !entry.isJar() && + !entry.isAar() && + !entry.isWar() && + !entry.isEar() && + !entry.isJmod() && !entry.isZip()) { System.out.println("Note: you're writing the processed class files to a directory [" + entry.getName() + "]."); diff --git a/src/proguard/ConfigurationConstants.java b/core/src/proguard/ConfigurationConstants.java similarity index 89% rename from src/proguard/ConfigurationConstants.java rename to core/src/proguard/ConfigurationConstants.java index ab41e6a4a..407d7c3fc 100644 --- a/src/proguard/ConfigurationConstants.java +++ b/core/src/proguard/ConfigurationConstants.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -37,6 +37,7 @@ class ConfigurationConstants public static final String LIBRARYJARS_OPTION = "-libraryjars"; public static final String RESOURCEJARS_OPTION = "-resourcejars"; + public static final String IF_OPTION = "-if"; 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"; @@ -44,6 +45,7 @@ class ConfigurationConstants 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 INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION = "includedescriptorclasses"; + public static final String INCLUDE_CODE_SUBOPTION = "includecode"; public static final String ALLOW_SHRINKING_SUBOPTION = "allowshrinking"; public static final String ALLOW_OPTIMIZATION_SUBOPTION = "allowoptimization"; public static final String ALLOW_OBFUSCATION_SUBOPTION = "allowobfuscation"; @@ -53,12 +55,15 @@ class ConfigurationConstants 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_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 ASSUME_NO_EXTERNAL_SIDE_EFFECTS_OPTION = "-assumenoexternalsideeffects"; + public static final String ASSUME_NO_ESCAPING_PARAMETERS_OPTION = "-assumenoescapingparameters"; + public static final String ASSUME_NO_EXTERNAL_RETURN_VALUES_OPTION = "-assumenoexternalreturnvalues"; + 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"; @@ -82,6 +87,7 @@ class ConfigurationConstants public static final String DONT_PREVERIFY_OPTION = "-dontpreverify"; public static final String MICRO_EDITION_OPTION = "-microedition"; + public static final String ANDROID_OPTION = "-android"; public static final String VERBOSE_OPTION = "-verbose"; public static final String DONT_NOTE_OPTION = "-dontnote"; @@ -89,6 +95,7 @@ class ConfigurationConstants 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 ADD_CONFIGURATION_DEBUGGING_OPTION = "-addconfigurationdebugging"; 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"; @@ -96,6 +103,7 @@ class ConfigurationConstants public static final String KEEP_DIRECTORIES_OPTION = "-keepdirectories"; public static final String FORCE_PROCESSING_OPTION = "-forceprocessing"; + public static final String ANY_FILE_KEYWORD = "**"; public static final String ANY_ATTRIBUTE_KEYWORD = "*"; diff --git a/src/proguard/ConfigurationParser.java b/core/src/proguard/ConfigurationParser.java similarity index 70% rename from src/proguard/ConfigurationParser.java rename to core/src/proguard/ConfigurationParser.java index e6a09ba95..0ba266601 100644 --- a/src/proguard/ConfigurationParser.java +++ b/core/src/proguard/ConfigurationParser.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,10 +22,10 @@ import proguard.classfile.*; import proguard.classfile.util.ClassUtil; -import proguard.util.ListUtil; +import proguard.util.*; import java.io.*; -import java.net.URL; +import java.net.*; import java.util.*; @@ -147,71 +147,77 @@ public void parse(Configuration configuration) // First include directives. if (ConfigurationConstants.AT_DIRECTIVE .startsWith(nextWord) || - ConfigurationConstants.INCLUDE_DIRECTIVE .startsWith(nextWord)) configuration.lastModified = parseIncludeArgument(configuration.lastModified); + 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.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(); + 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.IF_OPTION .startsWith(nextWord)) configuration.keep = parseIfCondition(configuration.keep); + else if (ConfigurationConstants.KEEP_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, true, false, false, null); + else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, false, false, null); + else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, false, null); + else if (ConfigurationConstants.KEEP_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, true, false, true, null); + else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, false, true, null); + else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION .startsWith(nextWord)) configuration.keep = parseKeepClassSpecificationArguments(configuration.keep, false, true, true, null); + 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 if (ConfigurationConstants.KEEP_DIRECTORIES_OPTION .startsWith(nextWord)) configuration.keepDirectories = parseCommaSeparatedList("directory name", true, true, false, true, false, true, 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(true, 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, true, false, false, false, configuration.optimizations); + else if (ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION .startsWith(nextWord)) configuration.assumeNoSideEffects = parseClassSpecificationArguments(true, configuration.assumeNoSideEffects); + else if (ConfigurationConstants.ASSUME_NO_EXTERNAL_SIDE_EFFECTS_OPTION .startsWith(nextWord)) configuration.assumeNoExternalSideEffects = parseClassSpecificationArguments(true, configuration.assumeNoExternalSideEffects); + else if (ConfigurationConstants.ASSUME_NO_ESCAPING_PARAMETERS_OPTION .startsWith(nextWord)) configuration.assumeNoEscapingParameters = parseClassSpecificationArguments(true, configuration.assumeNoEscapingParameters); + else if (ConfigurationConstants.ASSUME_NO_EXTERNAL_RETURN_VALUES_OPTION .startsWith(nextWord)) configuration.assumeNoExternalReturnValues = parseClassSpecificationArguments(true, configuration.assumeNoExternalReturnValues); + 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 = parseURL(); + else if (ConfigurationConstants.CLASS_OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.classObfuscationDictionary = parseURL(); + else if (ConfigurationConstants.PACKAGE_OBFUSCATION_DICTIONARY_OPTION .startsWith(nextWord)) configuration.packageObfuscationDictionary = parseURL(); + 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, 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, 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, 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, true, false, false, false, configuration.adaptResourceFileNames); + else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION .startsWith(nextWord)) configuration.adaptResourceFileContents = parseCommaSeparatedList("resource file name", true, true, false, true, false, true, false, false, false, configuration.adaptResourceFileContents); + + else if (ConfigurationConstants.DONT_PREVERIFY_OPTION .startsWith(nextWord)) configuration.preverify = parseNoArgument(false); + else if (ConfigurationConstants.MICRO_EDITION_OPTION .startsWith(nextWord)) configuration.microEdition = parseNoArgument(true); + else if (ConfigurationConstants.ANDROID_OPTION .startsWith(nextWord)) configuration.android = 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, false, true, false, configuration.note); + else if (ConfigurationConstants.DONT_WARN_OPTION .startsWith(nextWord)) configuration.warn = parseCommaSeparatedList("class name", true, true, false, false, true, false, 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 if (ConfigurationConstants.ADD_CONFIGURATION_DEBUGGING_OPTION .startsWith(nextWord)) configuration.addConfigurationDebugging = parseNoArgument(true); else { throw new ParseException("Unknown option " + reader.locationDescription()); @@ -220,7 +226,6 @@ public void parse(Configuration configuration) } - /** * Closes the configuration. * @throws IOException if an IO error occurs while closing the configuration. @@ -237,21 +242,59 @@ public void close() throws IOException private long parseIncludeArgument(long lastModified) throws ParseException, IOException { // Read the configuration file name. - readNextWord("configuration file name", true, false); + readNextWord("configuration file name", true, true, false); - File file = file(nextWord); - reader.includeWordReader(new FileWordReader(file)); + URL baseURL = reader.getBaseURL(); + URL url = null; + + try + { + // Check if the file name is a valid URL. + url = new URL(nextWord); + } + catch (MalformedURLException ex) {} + + if (url != null) + { + reader.includeWordReader(new FileWordReader(url)); + } + // Is it relative to a URL or to a file? + else if (baseURL != null) + { + url = new URL(baseURL, nextWord); + reader.includeWordReader(new FileWordReader(url)); + } + else + { + // Is the file a valid resource URL? + url = ConfigurationParser.class.getResource(nextWord); + if (url != null) + { + reader.includeWordReader(new FileWordReader(url)); + } + else + { + File file = file(nextWord); + reader.includeWordReader(new FileWordReader(file)); + + long fileLastModified = file.lastModified(); + if (fileLastModified > lastModified) + { + lastModified = fileLastModified; + } + } + } readNextWord(); - return Math.max(lastModified, file.lastModified()); + return lastModified; } private void parseBaseDirectoryArgument() throws ParseException, IOException { // Read the base directory name. - readNextWord("base directory name", true, false); + readNextWord("base directory name", true, true, false); reader.setBaseDir(file(nextWord)); @@ -272,7 +315,7 @@ private ClassPath parseClassPathArgument(ClassPath classPath, while (true) { // Read the next jar name. - readNextWord("jar or directory name", true, false); + readNextWord("jar or directory name", true, false, false); // Create a new class path entry. ClassPathEntry entry = new ClassPathEntry(file(nextWord), isOutput); @@ -292,7 +335,7 @@ private ClassPath parseClassPathArgument(ClassPath classPath, { // Read the filter. filters[counter++] = - parseCommaSeparatedList("filter", true, true, true, true, false, true, false, false, null); + parseCommaSeparatedList("filter", true, true, true, true, false, true, true, false, false, null); } while (counter < filters.length && ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)); @@ -319,17 +362,21 @@ private ClassPath parseClassPathArgument(ClassPath classPath, entry.setEarFilter(filters[--counter]); if (counter > 0) { - entry.setZipFilter(filters[--counter]); + entry.setJmodFilter(filters[--counter]); if (counter > 0) { - // For backward compatibility, the apk - // filter comes second in the list. - entry.setApkFilter(filters[--counter]); + entry.setZipFilter(filters[--counter]); if (counter > 0) { - // For backward compatibility, the aar - // filter comes first in the list. - entry.setAarFilter(filters[--counter]); + // For backward compatibility, the apk + // filter comes second in the list. + entry.setApkFilter(filters[--counter]); + if (counter > 0) + { + // For backward compatibility, the aar + // filter comes first in the list. + entry.setAarFilter(filters[--counter]); + } } } } @@ -385,6 +432,11 @@ private int parseIntegerArgument() readNextWord("integer"); int integer = Integer.parseInt(nextWord); + if (integer <= 0) + { + throw new ParseException("Expecting value larger than 0, instead of '" + nextWord + + "' before " + reader.locationDescription()); + } readNextWord(); @@ -398,11 +450,26 @@ private int parseIntegerArgument() } + private URL parseURL() + throws ParseException, IOException + { + // Read the obligatory file name. + readNextWord("file name", true, true, false); + + // Make sure the file is properly resolved. + URL url = url(nextWord); + + readNextWord(); + + return url; + } + + private File parseFile() throws ParseException, IOException { // Read the obligatory file name. - readNextWord("file name", true, false); + readNextWord("file name", true, true, false); // Make sure the file is properly resolved. File file = file(nextWord); @@ -417,7 +484,7 @@ private File parseOptionalFile() throws ParseException, IOException { // Read the optional file name. - readNextWord(true); + readNextWord(true, true); // Didn't the user specify a file name? if (configurationEnd()) @@ -469,10 +536,46 @@ private long parseNoArgument(long value) throws IOException } - private List parseKeepClassSpecificationArguments(List keepClassSpecifications, - boolean markClasses, - boolean markConditionally, - boolean allowShrinking) + private List parseIfCondition(List keepClassSpecifications) + throws ParseException, IOException + { + // Read the condition. + readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + + "', '" + JavaConstants.ACC_INTERFACE + + "', or '" + JavaConstants.ACC_ENUM + "'", + false, + false, + true); + + ClassSpecification condition = + parseClassSpecificationArguments(); + + // Read the corresponding keep option. + if (nextWord == null) + { + throw new ParseException("Expecting '-keep' option after '-if' option, before " + reader.locationDescription()); + } + + if (ConfigurationConstants.KEEP_OPTION .startsWith(nextWord)) keepClassSpecifications = parseKeepClassSpecificationArguments(keepClassSpecifications, true, false, false, condition); + else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION .startsWith(nextWord)) keepClassSpecifications = parseKeepClassSpecificationArguments(keepClassSpecifications, false, false, false, condition); + else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION .startsWith(nextWord)) keepClassSpecifications = parseKeepClassSpecificationArguments(keepClassSpecifications, false, true, false, condition); + else if (ConfigurationConstants.KEEP_NAMES_OPTION .startsWith(nextWord)) keepClassSpecifications = parseKeepClassSpecificationArguments(keepClassSpecifications, true, false, true, condition); + else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION .startsWith(nextWord)) keepClassSpecifications = parseKeepClassSpecificationArguments(keepClassSpecifications, false, false, true, condition); + else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION.startsWith(nextWord)) keepClassSpecifications = parseKeepClassSpecificationArguments(keepClassSpecifications, false, true, true, condition); + else + { + throw new ParseException("Expecting '-keep' option after '-if' option, before " + reader.locationDescription()); + } + + return keepClassSpecifications; + } + + + private List parseKeepClassSpecificationArguments(List keepClassSpecifications, + boolean markClasses, + boolean markConditionally, + boolean allowShrinking, + ClassSpecification condition) throws ParseException, IOException { // Create a new List if necessary. @@ -481,7 +584,23 @@ private List parseKeepClassSpecificationArguments(List keepClassSpecification keepClassSpecifications = new ArrayList(); } + // Read and add the keep configuration. + keepClassSpecifications.add(parseKeepClassSpecificationArguments(markClasses, + markConditionally, + allowShrinking, + condition)); + return keepClassSpecifications; + } + + + private KeepClassSpecification parseKeepClassSpecificationArguments(boolean markClasses, + boolean markConditionally, + boolean allowShrinking, + ClassSpecification condition) + throws ParseException, IOException + { boolean markDescriptorClasses = false; + boolean markCodeAttributes = false; //boolean allowShrinking = false; boolean allowOptimization = false; boolean allowObfuscation = false; @@ -492,7 +611,7 @@ private List parseKeepClassSpecificationArguments(List keepClassSpecification readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + "', '" + JavaConstants.ACC_INTERFACE + "', or '" + JavaConstants.ACC_ENUM + "'", - false, true); + false, false, true); if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord)) { @@ -508,6 +627,10 @@ private List parseKeepClassSpecificationArguments(List keepClassSpecification { markDescriptorClasses = true; } + else if (ConfigurationConstants.INCLUDE_CODE_SUBOPTION .startsWith(nextWord)) + { + markCodeAttributes = true; + } else if (ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION .startsWith(nextWord)) { allowShrinking = true; @@ -523,6 +646,7 @@ else if (ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION .startsWith( else { throw new ParseException("Expecting keyword '" + ConfigurationConstants.INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION + + "', '" + ConfigurationConstants.INCLUDE_CODE_SUBOPTION + "', '" + ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION + "', '" + ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION + "', or '" + ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION + @@ -534,19 +658,21 @@ else if (ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION .startsWith( ClassSpecification classSpecification = parseClassSpecificationArguments(); - // Create and add the keep configuration. - keepClassSpecifications.add(new KeepClassSpecification(markClasses, - markConditionally, - markDescriptorClasses, - allowShrinking, - allowOptimization, - allowObfuscation, - classSpecification)); - return keepClassSpecifications; + // Create and return the keep configuration. + return new KeepClassSpecification(markClasses, + markConditionally, + markDescriptorClasses, + markCodeAttributes, + allowShrinking, + allowOptimization, + allowObfuscation, + condition, + classSpecification); } - private List parseClassSpecificationArguments(List classSpecifications) + private List parseClassSpecificationArguments(boolean readFirstWord, + List classSpecifications) throws ParseException, IOException { // Create a new List if necessary. @@ -555,12 +681,15 @@ private List parseClassSpecificationArguments(List classSpecifications) classSpecifications = new ArrayList(); } - // Read and add the class configuration. - readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + - "', '" + JavaConstants.ACC_INTERFACE + - "', or '" + JavaConstants.ACC_ENUM + "'", - false, true); + if (readFirstWord) + { + readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + + "', '" + JavaConstants.ACC_INTERFACE + + "', or '" + JavaConstants.ACC_ENUM + "'", + false, false, true); + } + // Read and add the class configuration. classSpecifications.add(parseClassSpecificationArguments()); return classSpecifications; @@ -601,16 +730,15 @@ public ClassSpecification parseClassSpecificationArguments() strippedWord.equals(JavaConstants.ACC_INTERFACE) ? ClassConstants.ACC_INTERFACE : strippedWord.equals(JavaConstants.ACC_ABSTRACT) ? ClassConstants.ACC_ABSTRACT : strippedWord.equals(JavaConstants.ACC_SYNTHETIC) ? ClassConstants.ACC_SYNTHETIC : - strippedWord.equals(JavaConstants.ACC_ANNOTATION) ? ClassConstants.ACC_ANNOTATTION : + strippedWord.equals(JavaConstants.ACC_ANNOTATION) ? ClassConstants.ACC_ANNOTATION : strippedWord.equals(JavaConstants.ACC_ENUM) ? ClassConstants.ACC_ENUM : unknownAccessFlag(); // Is it an annotation modifier? - if (accessFlag == ClassConstants.ACC_ANNOTATTION) + if (accessFlag == ClassConstants.ACC_ANNOTATION) { - // Already read the next word. readNextWord("annotation type or keyword '" + JavaConstants.ACC_INTERFACE + "'", - false, false); + false, false, false); // Is the next word actually an annotation type? if (!nextWord.equals(JavaConstants.ACC_INTERFACE) && @@ -621,7 +749,7 @@ public ClassSpecification parseClassSpecificationArguments() annotationType = ListUtil.commaSeparatedString( parseCommaSeparatedList("annotation type", - false, false, false, false, true, false, false, true, null), false); + false, false, false, false, true, false, false, false, true, null), false); // Continue parsing the access modifier that we just read // in the next cycle. @@ -656,12 +784,12 @@ public ClassSpecification parseClassSpecificationArguments() } // Should we read the next word? - if (accessFlag != ClassConstants.ACC_ANNOTATTION) + if (accessFlag != ClassConstants.ACC_ANNOTATION) { readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + "', '" + JavaConstants.ACC_INTERFACE + "', or '" + JavaConstants.ACC_ENUM + "'", - false, true); + false, false, true); } } @@ -669,7 +797,7 @@ public ClassSpecification parseClassSpecificationArguments() String externalClassName = ListUtil.commaSeparatedString( parseCommaSeparatedList("class name or interface name", - true, false, false, false, true, false, false, false, null), false); + true, false, false, false, true, false, false, false, false, null), false); // For backward compatibility, allow a single "*" wildcard to match any // class. @@ -687,7 +815,7 @@ public ClassSpecification parseClassSpecificationArguments() if (ConfigurationConstants.IMPLEMENTS_KEYWORD.equals(nextWord) || ConfigurationConstants.EXTENDS_KEYWORD.equals(nextWord)) { - readNextWord("class name or interface name", false, true); + readNextWord("class name or interface name", false, false, true); // Parse the annotation type, if any. if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord)) @@ -695,13 +823,13 @@ public ClassSpecification parseClassSpecificationArguments() extendsAnnotationType = ListUtil.commaSeparatedString( parseCommaSeparatedList("annotation type", - true, false, false, false, true, false, false, true, null), false); + true, false, false, false, true, false, 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); + false, false, false, false, true, false, false, false, false, null), false); extendsClassName = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalExtendsClassName) ? null : @@ -735,7 +863,7 @@ public ClassSpecification parseClassSpecificationArguments() { readNextWord("class member description" + " or closing '" + ConfigurationConstants.CLOSE_KEYWORD + "'", - false, true); + false, false, true); if (nextWord.equals(ConfigurationConstants.CLOSE_KEYWORD)) { @@ -773,7 +901,7 @@ private void parseMemberSpecificationArguments(String externalClassN annotationType = ListUtil.commaSeparatedString( parseCommaSeparatedList("annotation type", - true, false, false, false, true, false, false, true, null), false); + true, false, false, false, true, false, false, false, true, null), false); continue; } @@ -953,7 +1081,7 @@ else if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord)) // Parse the method arguments. String descriptor = ClassUtil.internalMethodDescriptor(type, - parseCommaSeparatedList("argument", true, true, true, false, true, false, false, false, null)); + parseCommaSeparatedList("argument", true, true, true, false, true, false, false, false, false, null)); if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord)) { @@ -990,25 +1118,64 @@ else if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord)) } + /** + * Reads a comma-separated list of Lists of java identifiers or of file + * names. + */ + private List parseCommaSeparatedLists(String expectedDescription, + boolean readFirstWord, + boolean allowEmptyList, + boolean expectClosingParenthesis, + boolean isFileName, + boolean checkJavaIdentifiers, + boolean allowGenerics, + boolean replaceSystemProperties, + boolean replaceExternalClassNames, + boolean replaceExternalTypes, + List list) + throws ParseException, IOException + { + if (list == null) + { + list = new ArrayList(); + } + + // Parse a new list and add it to our list. + list.add(parseCommaSeparatedList(expectedDescription, + readFirstWord, + allowEmptyList, + expectClosingParenthesis, + isFileName, + checkJavaIdentifiers, + allowGenerics, + replaceSystemProperties, + replaceExternalClassNames, + replaceExternalTypes, + null)); + return list; + } + + /** * Reads a comma-separated list of java identifiers or of file names. * Examples of invocation arguments: - * ("directory name", true, true, false, true, false, true, false, false, ...) - * ("optimization", true, false, false, false, false, false, false, false, ...) - * ("package name", true, true, false, false, true, false, true, false, ...) - * ("attribute name", true, true, false, false, true, false, false, false, ...) - * ("class name", true, true, false, false, true, false, true, false, ...) - * ("resource file", true, true, false, true, false, false, false, false, ...) - * ("resource file", 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, ...) + * + * expected read allow expect is check allow replace replace replace + * description First empty Closing File Java Generic System Extern Extern + * Word List Paren Name Id Prop Class Types + * ---------------------------------------------------------------------------------- + * ("directory name", true, true, false, true, false, true, true, false, false, ...) + * ("optimization", true, false, false, false, false, true, false, false, false, ...) + * ("package name", true, true, false, false, true, false, false, true, false, ...) + * ("attribute name", true, true, false, false, true, false, false, false, false, ...) + * ("class name", true, true, false, false, true, false, false, true, false, ...) + * ("filter", true, true, true, true, false, true, true, false, false, ...) + * ("annotation ", false, false, false, false, true, false, false, false, true, ...) + * ("class name ", true, false, false, false, true, false, false, false, false, ...) + * ("annotation ", true, false, false, false, true, false, false, false, true, ...) + * ("class name ", false, false, false, false, true, false, false, false, false, ...) + * ("annotation ", true, false, false, false, true, false, false, false, true, ...) + * ("argument", true, true, true, false, true, false, false, false, false, ...) */ private List parseCommaSeparatedList(String expectedDescription, boolean readFirstWord, @@ -1016,6 +1183,7 @@ private List parseCommaSeparatedList(String expectedDescription, boolean expectClosingParenthesis, boolean isFileName, boolean checkJavaIdentifiers, + boolean allowGenerics, boolean replaceSystemProperties, boolean replaceExternalClassNames, boolean replaceExternalTypes, @@ -1032,12 +1200,12 @@ private List parseCommaSeparatedList(String expectedDescription, if (!allowEmptyList) { // Read the first list entry. - readNextWord(expectedDescription, isFileName, false); + readNextWord(expectedDescription, isFileName, true, false); } else if (expectClosingParenthesis) { // Read the first list entry. - readNextWord(expectedDescription, isFileName, false); + readNextWord(expectedDescription, isFileName, true, false); // Return if the entry is actually empty (an empty file name or // a closing parenthesis). @@ -1057,7 +1225,7 @@ else if (nextWord.equals(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD)) else { // Read the first list entry, if there is any. - readNextWord(isFileName); + readNextWord(isFileName, true); // Check if the list is empty. if (configurationEnd()) @@ -1071,7 +1239,7 @@ else if (nextWord.equals(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD)) { if (checkJavaIdentifiers) { - checkJavaIdentifier("java type"); + checkJavaIdentifier("java type", allowGenerics); } if (replaceSystemProperties) @@ -1110,7 +1278,7 @@ else if (nextWord.equals(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD)) } // Read the next list entry. - readNextWord(expectedDescription, isFileName, false); + readNextWord(expectedDescription, isFileName, true, false); } } @@ -1124,6 +1292,50 @@ private int unknownAccessFlag() throws ParseException } + /** + * Creates a properly resolved URL, based on the given word. + */ + private URL url(String word) throws ParseException, MalformedURLException + { + String fileName = replaceSystemProperties(word); + URL url; + + try + { + // Check if the file name is a valid URL. + url = new URL(fileName); + return url; + } + catch (MalformedURLException ex) {} + + // Is it relative to a URL or to a file? + URL baseURL = reader.getBaseURL(); + if (baseURL != null) + { + url = new URL(baseURL, fileName); + } + else + { + // Is the file a valid resource URL? + url = ConfigurationParser.class.getResource(fileName); + if (url == null) + { + File file = new File(fileName); + + // Try to get an absolute file. + if (!file.isAbsolute()) + { + file = new File(reader.getBaseDir(), fileName); + } + + url = file.toURI().toURL(); + } + } + + return url; + } + + /** * Creates a properly resolved File, based on the given word. */ @@ -1187,7 +1399,7 @@ private String replaceSystemProperties(String word) throws ParseException private void readNextWord(String expectedDescription) throws ParseException, IOException { - readNextWord(expectedDescription, false, false); + readNextWord(expectedDescription, false, false, false); } @@ -1197,10 +1409,11 @@ private void readNextWord(String expectedDescription) */ private void readNextWord(String expectedDescription, boolean isFileName, + boolean expectSingleFile, boolean expectingAtCharacter) throws ParseException, IOException { - readNextWord(isFileName); + readNextWord(isFileName, expectSingleFile); if (configurationEnd(expectingAtCharacter)) { throw new ParseException("Expecting " + expectedDescription + @@ -1214,16 +1427,17 @@ private void readNextWord(String expectedDescription, */ private void readNextWord() throws IOException { - readNextWord(false); + readNextWord(false, false); } /** * Reads the next word of the configuration in the 'nextWord' field. */ - private void readNextWord(boolean isFileName) throws IOException + private void readNextWord(boolean isFileName, + boolean expectSingleFile) throws IOException { - nextWord = reader.nextWord(isFileName); + nextWord = reader.nextWord(isFileName, expectSingleFile); } @@ -1253,6 +1467,17 @@ private boolean configurationEnd(boolean expectingAtCharacter) * a ParseException if it isn't. Wildcard characters are accepted. */ private void checkJavaIdentifier(String expectedDescription) + throws ParseException + { + checkJavaIdentifier(expectedDescription, true); + } + + + /** + * 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, boolean allowGenerics) throws ParseException { if (!isJavaIdentifier(nextWord)) @@ -1260,6 +1485,13 @@ private void checkJavaIdentifier(String expectedDescription) throw new ParseException("Expecting " + expectedDescription + " before " + reader.locationDescription()); } + + if (!allowGenerics && containsGenerics(nextWord)) + { + throw new ParseException("Use of generics not allowed for " + + expectedDescription + + " at " + reader.locationDescription()); + } } @@ -1297,6 +1529,43 @@ private boolean isJavaIdentifier(String aWord) } + private boolean containsGenerics(String aWord) + { + return containsGenerics(aWord, 0); + } + + + /** + * Returns whether the given word contains angle brackets around + * a non-digit string. + */ + private boolean containsGenerics(String aWord, int startIndex) + { + int openIndex = aWord.indexOf('<', startIndex); + if (openIndex < 0) + { + return false; + } + + int closeIndex = aWord.indexOf('>', startIndex + openIndex + 1); + if (closeIndex < 0) + { + return false; + } + + try + { + Integer.parseInt(aWord.substring(openIndex + 1, closeIndex)); + } + catch (NumberFormatException e) + { + return true; + } + + return containsGenerics(aWord,closeIndex); + } + + /** * Checks whether the given access flags are valid field access flags, * throwing a ParseException if they aren't. diff --git a/src/proguard/ConfigurationWriter.java b/core/src/proguard/ConfigurationWriter.java similarity index 85% rename from src/proguard/ConfigurationWriter.java rename to core/src/proguard/ConfigurationWriter.java index 648f639aa..04a3b29fd 100644 --- a/src/proguard/ConfigurationWriter.java +++ b/core/src/proguard/ConfigurationWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,9 +25,9 @@ import proguard.util.ListUtil; import java.io.*; +import java.net.*; import java.util.*; - /** * This class writes ProGuard configurations to a file. * @@ -52,7 +52,9 @@ public class ConfigurationWriter */ public ConfigurationWriter(File configurationFile) throws IOException { - this(new PrintWriter(new FileWriter(configurationFile))); + this(new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(configurationFile), "UTF-8"))); baseDir = configurationFile.getParentFile(); } @@ -120,46 +122,53 @@ public void write(Configuration configuration) throws IOException 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); + 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.ANDROID_OPTION, configuration.android); + + 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.ADD_CONFIGURATION_DEBUGGING_OPTION, configuration.addConfigurationDebugging); + + 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); + writer.println(); // 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); + writeOptions(ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION, configuration.assumeNoSideEffects); + writeOptions(ConfigurationConstants.ASSUME_NO_EXTERNAL_SIDE_EFFECTS_OPTION, configuration.assumeNoExternalSideEffects); + writeOptions(ConfigurationConstants.ASSUME_NO_ESCAPING_PARAMETERS_OPTION, configuration.assumeNoEscapingParameters); + writeOptions(ConfigurationConstants.ASSUME_NO_EXTERNAL_RETURN_VALUES_OPTION, configuration.assumeNoExternalReturnValues); + if (writer.checkError()) { @@ -193,6 +202,7 @@ private void writeJarOptions(String inputEntryOptionName, filtered = writeFilter(filtered, entry.getAarFilter()); filtered = writeFilter(filtered, entry.getApkFilter()); filtered = writeFilter(filtered, entry.getZipFilter()); + filtered = writeFilter(filtered, entry.getJmodFilter()); filtered = writeFilter(filtered, entry.getEarFilter()); filtered = writeFilter(filtered, entry.getWarFilter()); filtered = writeFilter(filtered, entry.getJarFilter()); @@ -307,6 +317,37 @@ private void writeOption(String optionName, } + private void writeOption(String optionName, URL url) + { + if (url != null) + { + if (url.getPath().length() > 0) + { + String fileName = url.toExternalForm(); + if (url.getProtocol().equals("file")) + { + try + { + fileName = relativeFileName(new File(url.toURI())); + } + catch (URISyntaxException ignore) {} + } + else + { + } + + writer.print(optionName); + writer.print(' '); + writer.println(fileName); + } + else + { + writer.println(optionName); + } + } + } + + private void writeOption(String optionName, File file) { if (file != null) @@ -341,6 +382,11 @@ private void writeOptions(String[] optionNames, private void writeOption(String[] optionNames, KeepClassSpecification keepClassSpecification) { + if (keepClassSpecification.condition != null) + { + writeOption(ConfigurationConstants.IF_OPTION, keepClassSpecification.condition); + } + // Compose the option name. String optionName = optionNames[keepClassSpecification.markConditionally ? 2 : keepClassSpecification.markClasses ? 0 : @@ -352,6 +398,12 @@ private void writeOption(String[] optionNames, ConfigurationConstants.INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION; } + if (keepClassSpecification.markCodeAttributes) + { + optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + + ConfigurationConstants.INCLUDE_CODE_SUBOPTION; + } + if (keepClassSpecification.allowShrinking) { optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + @@ -418,7 +470,8 @@ private void writeOption(String optionName, if (((classSpecification.requiredSetAccessFlags | classSpecification.requiredUnsetAccessFlags) & (ClassConstants.ACC_INTERFACE | - ClassConstants.ACC_ENUM)) == 0) + ClassConstants.ACC_ENUM | + ClassConstants.ACC_MODULE)) == 0) { writer.print(ConfigurationConstants.CLASS_KEYWORD); } diff --git a/src/proguard/DataEntryReaderFactory.java b/core/src/proguard/DataEntryReaderFactory.java similarity index 51% rename from src/proguard/DataEntryReaderFactory.java rename to core/src/proguard/DataEntryReaderFactory.java index 3bdd22014..dcbdbe359 100644 --- a/src/proguard/DataEntryReaderFactory.java +++ b/core/src/proguard/DataEntryReaderFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,6 +20,7 @@ */ package proguard; +import proguard.classfile.ClassConstants; import proguard.io.*; import proguard.util.*; @@ -29,7 +30,7 @@ /** * 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. + * jmods, and zips before passing them to a given reader. * * @author Eric Lafortune */ @@ -48,37 +49,41 @@ public static DataEntryReader createDataEntryReader(String messagePrefi ClassPathEntry classPathEntry, DataEntryReader reader) { - boolean isApk = classPathEntry.isApk(); - boolean isJar = classPathEntry.isJar(); - boolean isAar = classPathEntry.isAar(); - boolean isWar = classPathEntry.isWar(); - boolean isEar = classPathEntry.isEar(); - boolean isZip = classPathEntry.isZip(); + boolean isApk = classPathEntry.isApk(); + boolean isJar = classPathEntry.isJar(); + boolean isAar = classPathEntry.isAar(); + boolean isWar = classPathEntry.isWar(); + boolean isEar = classPathEntry.isEar(); + boolean isJmod = classPathEntry.isJmod(); + boolean isZip = classPathEntry.isZip(); - List filter = classPathEntry.getFilter(); - List apkFilter = classPathEntry.getApkFilter(); - List jarFilter = classPathEntry.getJarFilter(); - List aarFilter = classPathEntry.getAarFilter(); - List warFilter = classPathEntry.getWarFilter(); - List earFilter = classPathEntry.getEarFilter(); - List zipFilter = classPathEntry.getZipFilter(); + List filter = classPathEntry.getFilter(); + List apkFilter = classPathEntry.getApkFilter(); + List jarFilter = classPathEntry.getJarFilter(); + List aarFilter = classPathEntry.getAarFilter(); + List warFilter = classPathEntry.getWarFilter(); + List earFilter = classPathEntry.getEarFilter(); + List jmodFilter = classPathEntry.getJmodFilter(); + List zipFilter = classPathEntry.getZipFilter(); System.out.println(messagePrefix + - (isApk ? "apk" : - isJar ? "jar" : - isAar ? "aar" : - isWar ? "war" : - isEar ? "ear" : - isZip ? "zip" : + (isApk ? "apk" : + isJar ? "jar" : + isAar ? "aar" : + isWar ? "war" : + isEar ? "ear" : + isJmod ? "jmod" : + isZip ? "zip" : "directory") + " [" + classPathEntry.getName() + "]" + - (filter != null || - apkFilter != null || - jarFilter != null || - aarFilter != null || - warFilter != null || - earFilter != null || - zipFilter != null ? " (filtered)" : "")); + (filter != null || + apkFilter != null || + jarFilter != null || + aarFilter != null || + warFilter != null || + earFilter != null || + jmodFilter != null || + zipFilter != null ? " (filtered)" : "")); // Add a filter, if specified. if (filter != null) @@ -90,27 +95,32 @@ public static DataEntryReader createDataEntryReader(String messagePrefi } // Unzip any apks, if necessary. - reader = wrapInJarReader(reader, isApk, apkFilter, ".apk"); + reader = wrapInJarReader(reader, false, isApk, apkFilter, ".apk"); if (!isApk) { // Unzip any jars, if necessary. - reader = wrapInJarReader(reader, isJar, jarFilter, ".jar"); + reader = wrapInJarReader(reader, false, isJar, jarFilter, ".jar"); if (!isJar) { // Unzip any aars, if necessary. - reader = wrapInJarReader(reader, isAar, aarFilter, ".aar"); + reader = wrapInJarReader(reader, false, isAar, aarFilter, ".aar"); if (!isAar) { // Unzip any wars, if necessary. - reader = wrapInJarReader(reader, isWar, warFilter, ".war"); + reader = wrapInJarReader(reader, false, isWar, warFilter, ".war"); if (!isWar) { // Unzip any ears, if necessary. - reader = wrapInJarReader(reader, isEar, earFilter, ".ear"); + reader = wrapInJarReader(reader, false, isEar, earFilter, ".ear"); if (!isEar) { - // Unzip any zips, if necessary. - reader = wrapInJarReader(reader, isZip, zipFilter, ".zip"); + // Unzip any jmods, if necessary. + reader = wrapInJarReader(reader, true, isJmod, jmodFilter, ".jmod"); + if (!isJmod) + { + // Unzip any zips, if necessary. + reader = wrapInJarReader(reader, false, isZip, zipFilter, ".zip"); + } } } } @@ -122,15 +132,37 @@ public static DataEntryReader createDataEntryReader(String messagePrefi /** - * Wraps the given DataEntryReader in a JarReader, filtering it if necessary. + * Wraps the given DataEntryReader in a JarReader, filtering it if necessary. + * @param reader the data entry reader that can read the + * entries contained in the jar file. + * @param isJmod specifies whether to strip the "classes/" + * prefix from contained .class data entries + * and the jmod magic bytes from the zip. + * @param isJar specifies whether the data entries should + * always be unzipped. + * @param jarFilter otherwise, an optional filter on the data + * entry names. + * @param jarExtension also otherwise, a required data entry name + * extension. + * @return a DataEntryReader for reading the entries of jar file data + * entries. */ private static DataEntryReader wrapInJarReader(DataEntryReader reader, + boolean isJmod, boolean isJar, List jarFilter, String jarExtension) { + if (isJmod) + { + reader = new FilteredDataEntryReader( + new DataEntryNameFilter(new ExtensionMatcher(".class")), + new PrefixStrippingDataEntryReader("classes/", reader), + reader); + } + // Unzip any jars, if necessary. - DataEntryReader jarReader = new JarReader(reader); + DataEntryReader jarReader = new JarReader(reader, isJmod); if (isJar) { diff --git a/core/src/proguard/DataEntryWriterFactory.java b/core/src/proguard/DataEntryWriterFactory.java new file mode 100644 index 000000000..4cb47290d --- /dev/null +++ b/core/src/proguard/DataEntryWriterFactory.java @@ -0,0 +1,211 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.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 apks, jars, wars, ears, jmods, + * and zips. + * + * @author Eric Lafortune + */ +public class DataEntryWriterFactory +{ + private final ClassPool programClassPool; + private final MultiValueMap extraClassNameMap; + + + /** + * Creates a new DataEntryWriterFactory with the given parameters. + * @param programClassPool the program classpool to process. + */ + public DataEntryWriterFactory(ClassPool programClassPool, + MultiValueMap extraClassNamemap) + { + this.programClassPool = programClassPool; + this.extraClassNameMap = extraClassNamemap; + } + + + /** + * 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 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 DataEntryWriter createClassPathEntryWriter(ClassPathEntry classPathEntry, + DataEntryWriter alternativeWriter) + { + boolean isApk = classPathEntry.isApk(); + boolean isJar = classPathEntry.isJar(); + boolean isAar = classPathEntry.isAar(); + boolean isWar = classPathEntry.isWar(); + boolean isEar = classPathEntry.isEar(); + boolean isJmod = classPathEntry.isJmod(); + boolean isZip = classPathEntry.isZip(); + + List filter = classPathEntry.getFilter(); + List apkFilter = classPathEntry.getApkFilter(); + List jarFilter = classPathEntry.getJarFilter(); + List aarFilter = classPathEntry.getAarFilter(); + List warFilter = classPathEntry.getWarFilter(); + List earFilter = classPathEntry.getEarFilter(); + List jmodFilter = classPathEntry.getJmodFilter(); + List zipFilter = classPathEntry.getZipFilter(); + + System.out.println("Preparing output " + + (isApk ? "apk" : + isJar ? "jar" : + isAar ? "aar" : + isWar ? "war" : + isEar ? "ear" : + isJmod ? "jmod" : + isZip ? "zip" : + "directory") + + " [" + classPathEntry.getName() + "]" + + (filter != null || + apkFilter != null || + jarFilter != null || + aarFilter != null || + warFilter != null || + earFilter != null || + jmodFilter != null || + zipFilter != null ? " (filtered)" : "")); + + DataEntryWriter writer = new DirectoryWriter(classPathEntry.getFile(), + isApk || + isJar || + isAar || + isWar || + isEar || + isJmod || + isZip); + + // Set up the filtered jar writers. + writer = wrapInJarWriter(writer, false, null, isZip, zipFilter, ".zip", isApk || isJar || isAar || isWar || isEar || isJmod); + writer = wrapInJarWriter(writer, true, ClassConstants.JMOD_HEADER, isJmod, jmodFilter, ".jmod", isApk || isJar || isAar || isWar || isEar); + writer = wrapInJarWriter(writer, false, null, isEar, earFilter, ".ear", isApk || isJar || isAar || isWar); + writer = wrapInJarWriter(writer, true, null, isWar, warFilter, ".war", isApk || isJar || isAar); + writer = wrapInJarWriter(writer, false, null, isAar, aarFilter, ".aar", isApk || isJar); + writer = wrapInJarWriter(writer, false, null, isJar, jarFilter, ".jar", isApk); + writer = wrapInJarWriter(writer, false, null, isApk, apkFilter, ".apk", false); + + // Set up for writing out the program classes. + writer = new ClassDataEntryWriter(programClassPool, writer); + + // Add a filter, if specified. + writer = filter != null ? + new FilteredDataEntryWriter( + new DataEntryNameFilter( + new ListParser(new FileNameParser()).parse(filter)), + writer) : + writer; + + // Add a writer for the injected classes. + writer = new ExtraDataEntryWriter(extraClassNameMap, + writer, + writer, + ClassConstants.CLASS_FILE_EXTENSION); + + // 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 DataEntryWriter wrapInJarWriter(DataEntryWriter writer, + boolean addClassesPrefix, + byte[] header, + boolean isJar, + List jarFilter, + String jarExtension, + boolean dontWrap) + { + // Zip up jars, if necessary. + DataEntryWriter jarWriter = + dontWrap ? + new ParentDataEntryWriter(writer) : + new JarWriter(header, writer); + + // Add a "classes/" prefix for class files, if specified. + if (addClassesPrefix) + { + writer = new FilteredDataEntryWriter( + new DataEntryNameFilter( + new ExtensionMatcher(".class")), + new PrefixAddingDataEntryWriter("classes/", + writer), + 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/core/src/proguard/DescriptorKeepChecker.java similarity index 91% rename from src/proguard/DescriptorKeepChecker.java rename to core/src/proguard/DescriptorKeepChecker.java index e71197b16..a7817a587 100644 --- a/src/proguard/DescriptorKeepChecker.java +++ b/core/src/proguard/DescriptorKeepChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -74,12 +74,12 @@ public void checkClassSpecifications(List keepSpecifications) // Create a visitor for marking the seeds. KeepMarker keepMarker = new KeepMarker(); ClassPoolVisitor classPoolvisitor = - ClassSpecificationVisitorFactory.createClassPoolVisitor(keepSpecifications, - keepMarker, - keepMarker, - false, - true, - true); + new KeepClassSpecificationVisitorFactory(true, true, true) + .createClassPoolVisitor(keepSpecifications, + keepMarker, + keepMarker, + keepMarker, + null); // Mark the seeds. programClassPool.accept(classPoolvisitor); libraryClassPool.accept(classPoolvisitor); diff --git a/src/proguard/DuplicateClassPrinter.java b/core/src/proguard/DuplicateClassPrinter.java similarity index 97% rename from src/proguard/DuplicateClassPrinter.java rename to core/src/proguard/DuplicateClassPrinter.java index 947e3ae50..f8d0f4d9e 100644 --- a/src/proguard/DuplicateClassPrinter.java +++ b/core/src/proguard/DuplicateClassPrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/FileWordReader.java b/core/src/proguard/FileWordReader.java similarity index 96% rename from src/proguard/FileWordReader.java rename to core/src/proguard/FileWordReader.java index 9b8f0e890..2e32c470b 100644 --- a/src/proguard/FileWordReader.java +++ b/core/src/proguard/FileWordReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/FullyQualifiedClassNameChecker.java b/core/src/proguard/FullyQualifiedClassNameChecker.java similarity index 97% rename from src/proguard/FullyQualifiedClassNameChecker.java rename to core/src/proguard/FullyQualifiedClassNameChecker.java index f284349ca..1f5141941 100644 --- a/src/proguard/FullyQualifiedClassNameChecker.java +++ b/core/src/proguard/FullyQualifiedClassNameChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,7 +24,7 @@ import proguard.classfile.util.*; import proguard.classfile.visitor.*; -import java.util.List; +import java.util.*; /** * This class checks if the user has forgotten to fully qualify any classes @@ -186,7 +186,8 @@ private static boolean containsWildCards(String string) string.indexOf('*') >= 0 || string.indexOf('?') >= 0 || string.indexOf(',') >= 0 || - string.indexOf("///") >= 0); + string.indexOf("///") >= 0 || + string.indexOf('<') >= 0); } diff --git a/src/proguard/GPL.java b/core/src/proguard/GPL.java similarity index 99% rename from src/proguard/GPL.java rename to core/src/proguard/GPL.java index 6558470d7..635c3e1c3 100644 --- a/src/proguard/GPL.java +++ b/core/src/proguard/GPL.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/GetAnnotationChecker.java b/core/src/proguard/GetAnnotationChecker.java similarity index 98% rename from src/proguard/GetAnnotationChecker.java rename to core/src/proguard/GetAnnotationChecker.java index 6aad6cc66..c51131c18 100644 --- a/src/proguard/GetAnnotationChecker.java +++ b/core/src/proguard/GetAnnotationChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/GetEnclosingClassChecker.java b/core/src/proguard/GetEnclosingClassChecker.java similarity index 97% rename from src/proguard/GetEnclosingClassChecker.java rename to core/src/proguard/GetEnclosingClassChecker.java index b9c1105ca..b9d58aa40 100644 --- a/src/proguard/GetEnclosingClassChecker.java +++ b/core/src/proguard/GetEnclosingClassChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/GetEnclosingMethodChecker.java b/core/src/proguard/GetEnclosingMethodChecker.java similarity index 97% rename from src/proguard/GetEnclosingMethodChecker.java rename to core/src/proguard/GetEnclosingMethodChecker.java index 9926ea6ea..40edb35c2 100644 --- a/src/proguard/GetEnclosingMethodChecker.java +++ b/core/src/proguard/GetEnclosingMethodChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/GetSignatureChecker.java b/core/src/proguard/GetSignatureChecker.java similarity index 97% rename from src/proguard/GetSignatureChecker.java rename to core/src/proguard/GetSignatureChecker.java index cec97d04b..699f2bbe3 100644 --- a/src/proguard/GetSignatureChecker.java +++ b/core/src/proguard/GetSignatureChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/Initializer.java b/core/src/proguard/Initializer.java similarity index 91% rename from src/proguard/Initializer.java rename to core/src/proguard/Initializer.java index c93ba38be..4e51f4401 100644 --- a/src/proguard/Initializer.java +++ b/core/src/proguard/Initializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -29,11 +29,11 @@ import proguard.classfile.visitor.*; import proguard.util.*; -import java.io.IOException; +import java.io.*; import java.util.*; /** - * This class initializes class pools. + * This class initializes class pools and resource information. * * @author Eric Lafortune */ @@ -59,6 +59,11 @@ public Initializer(Configuration configuration) public void execute(ClassPool programClassPool, ClassPool libraryClassPool) throws IOException { + // We're using the system's default character encoding for writing to + // the standard output and error output. + PrintWriter out = new PrintWriter(System.out, true); + PrintWriter err = new PrintWriter(System.err, true); + int originalLibraryClassPoolSize = libraryClassPool.size(); // Perform basic checks on the configuration. @@ -71,6 +76,9 @@ public void execute(ClassPool programClassPool, fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.keep); fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoSideEffects); + fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoExternalSideEffects); + fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoEscapingParameters); + fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoExternalReturnValues); StringMatcher keepAttributesMatcher = configuration.keepAttributes != null ? new ListParser(new NameParser()).parse(configuration.keepAttributes) : @@ -188,7 +196,10 @@ public void execute(ClassPool programClassPool, dynamicClassReferenceNotePrinter, null, classForNameNotePrinter, - createClassNoteExceptionMatcher(configuration.keep)))))); + createClassNoteExceptionMatcher(configuration.keep, true)))))); + + // Initialize the WebView.addJavascriptInterface references. + WarningPrinter webViewClassReferenceNotePrinter = new WarningPrinter(System.out, configuration.note); // Initialize the Class.get[Declared]{Field,Method} references. WarningPrinter getMemberNotePrinter = new WarningPrinter(System.out, configuration.note); @@ -196,12 +207,11 @@ public void execute(ClassPool programClassPool, programClassPool.classesAccept( new AllMethodVisitor( new AllAttributeVisitor( - new AllInstructionVisitor( new DynamicMemberReferenceInitializer(programClassPool, libraryClassPool, getMemberNotePrinter, createClassMemberNoteExceptionMatcher(configuration.keep, true), - createClassMemberNoteExceptionMatcher(configuration.keep, false)))))); + createClassMemberNoteExceptionMatcher(configuration.keep, false))))); // Initialize other string constant references, if requested. if (configuration.adaptClassStrings != null) @@ -242,8 +252,7 @@ public void execute(ClassPool programClassPool, // classes and the library classes that are referenced by referenced // library classes. reducedLibraryClassPool.classesAccept( - new MultiClassVisitor(new ClassVisitor[] - { + new MultiClassVisitor( new ClassHierarchyTraveler(true, true, true, false, new LibraryClassFilter( new ClassPoolFiller(libraryClassPool))), @@ -253,7 +262,7 @@ public void execute(ClassPool programClassPool, new ClassHierarchyTraveler(true, true, true, false, new LibraryClassFilter( new ClassPoolFiller(libraryClassPool))))) - })); + )); } else { @@ -284,6 +293,9 @@ public void execute(ClassPool programClassPool, classMemberChecker.checkClassSpecifications(configuration.keep); classMemberChecker.checkClassSpecifications(configuration.assumeNoSideEffects); + classMemberChecker.checkClassSpecifications(configuration.assumeNoExternalSideEffects); + classMemberChecker.checkClassSpecifications(configuration.assumeNoEscapingParameters); + classMemberChecker.checkClassSpecifications(configuration.assumeNoExternalReturnValues); // Check for unkept descriptor classes of kept class members. WarningPrinter descriptorKeepNotePrinter = new WarningPrinter(System.out, configuration.note); @@ -496,7 +508,8 @@ public void execute(ClassPool programClassPool, * Extracts a list of exceptions of classes for which not to print notes, * from the keep configuration. */ - private StringMatcher createClassNoteExceptionMatcher(List noteExceptions) + private StringMatcher createClassNoteExceptionMatcher(List noteExceptions, + boolean markClasses) { if (noteExceptions != null) { @@ -504,18 +517,20 @@ private StringMatcher createClassNoteExceptionMatcher(List noteExceptions) for (int index = 0; index < noteExceptions.size(); index++) { KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); - if (keepClassSpecification.markClasses) + if (keepClassSpecification.markClasses || !markClasses) { // If the class itself is being kept, it's ok. String className = keepClassSpecification.className; - if (className != null) + if (className != null && + !containsWildCardReferences(className)) { noteExceptionNames.add(className); } // If all of its extensions are being kept, it's ok too. String extendsClassName = keepClassSpecification.extendsClassName; - if (extendsClassName != null) + if (extendsClassName != null && + !containsWildCardReferences(extendsClassName)) { noteExceptionNames.add(extendsClassName); } @@ -557,7 +572,8 @@ private StringMatcher createClassMemberNoteExceptionMatcher(List noteExceptio (MemberSpecification)memberSpecifications.get(index2); String memberName = memberSpecification.name; - if (memberName != null) + if (memberName != null && + !containsWildCardReferences(memberName)) { noteExceptionNames.add(memberName); } @@ -567,10 +583,41 @@ private StringMatcher createClassMemberNoteExceptionMatcher(List noteExceptio if (noteExceptionNames.size() > 0) { - return new ListParser(new ClassNameParser()).parse(noteExceptionNames); + return new ListParser(new NameParser()).parse(noteExceptionNames); } } return null; } + + + /** + * Returns whether the given string contains a numeric reference to a + * wild card (""). + */ + private static boolean containsWildCardReferences(String string) + { + int openIndex = string.indexOf('<'); + if (openIndex < 0) + { + return false; + } + + int closeIndex = string.indexOf('>', openIndex + 1); + if (closeIndex < 0) + { + return false; + } + + try + { + Integer.parseInt(string.substring(openIndex + 1, closeIndex)); + } + catch (NumberFormatException e) + { + return false; + } + + return true; + } } diff --git a/src/proguard/InputReader.java b/core/src/proguard/InputReader.java similarity index 83% rename from src/proguard/InputReader.java rename to core/src/proguard/InputReader.java index 341407489..34f98aa05 100644 --- a/src/proguard/InputReader.java +++ b/core/src/proguard/InputReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -34,6 +34,12 @@ */ public class InputReader { + // Option to favor library classes over program classes, in case of + // duplicates. + // https://sourceforge.net/p/proguard/discussion/182455/thread/76430d9e + private static final boolean FAVOR_LIBRARY_CLASSES = System.getProperty("favor.library.classes") != null; + + private final Configuration configuration; @@ -59,6 +65,24 @@ public void execute(ClassPool programClassPool, DuplicateClassPrinter duplicateClassPrinter = new DuplicateClassPrinter(notePrinter); + // Read the library class files, if any and if they should get priority. + if (FAVOR_LIBRARY_CLASSES && + 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(libraryClassPool, duplicateClassPrinter, + new ClassPoolFiller(libraryClassPool))))); + } + // Read the program class files. // Prepare a data entry reader to filter all classes, // which are then decoded to classes by a class reader, @@ -71,7 +95,8 @@ public void execute(ClassPool programClassPool, configuration.skipNonPublicLibraryClassMembers, warningPrinter, new ClassPresenceFilter(programClassPool, duplicateClassPrinter, - new ClassPoolFiller(programClassPool))))); + new ClassPresenceFilter(libraryClassPool, duplicateClassPrinter, + new ClassPoolFiller(programClassPool)))))); // Check if we have at least some input classes. if (programClassPool.size() == 0) @@ -80,7 +105,8 @@ public void execute(ClassPool programClassPool, } // Read the library class files, if any. - if (configuration.libraryJars != null) + if (!FAVOR_LIBRARY_CLASSES && + configuration.libraryJars != null) { // Prepare a data entry reader to filter all classes, // which are then decoded to classes by a class reader, @@ -170,7 +196,7 @@ private void readInput(String messagePrefix, { try { - // Create a reader that can unwrap jars, wars, ears, and zips. + // Create a reader that can unwrap jars, wars, ears, jmods and zips. DataEntryReader reader = DataEntryReaderFactory.createDataEntryReader(messagePrefix, classPathEntry, diff --git a/src/proguard/KeepClassMemberChecker.java b/core/src/proguard/KeepClassMemberChecker.java similarity index 98% rename from src/proguard/KeepClassMemberChecker.java rename to core/src/proguard/KeepClassMemberChecker.java index 38dcc5a89..d8fb25e0c 100644 --- a/src/proguard/KeepClassMemberChecker.java +++ b/core/src/proguard/KeepClassMemberChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/KeepClassSpecification.java b/core/src/proguard/KeepClassSpecification.java similarity index 63% rename from src/proguard/KeepClassSpecification.java rename to core/src/proguard/KeepClassSpecification.java index 26a5bc0f7..86e2a0f5a 100644 --- a/src/proguard/KeepClassSpecification.java +++ b/core/src/proguard/KeepClassSpecification.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,44 +27,14 @@ */ public class KeepClassSpecification extends ClassSpecification { - public final boolean markClasses; - public final boolean markConditionally; - public final boolean markDescriptorClasses; - 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 markDescriptorClasses specifies whether to mark the classes in - * the descriptors of the marked class members. - * @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 markDescriptorClasses, - boolean allowShrinking, - boolean allowOptimization, - boolean allowObfuscation) - { - this.markClasses = markClasses; - this.markConditionally = markConditionally; - this.markDescriptorClasses = markDescriptorClasses; - this.allowShrinking = allowShrinking; - this.allowOptimization = allowOptimization; - this.allowObfuscation = allowObfuscation; - } + public final boolean markClasses; + public final boolean markConditionally; + public final boolean markDescriptorClasses; + public final boolean markCodeAttributes; + public final boolean allowShrinking; + public final boolean allowOptimization; + public final boolean allowObfuscation; + public final ClassSpecification condition; /** @@ -79,18 +49,23 @@ public KeepClassSpecification(boolean markClasses, * members are present. * @param markDescriptorClasses specifies whether to mark the classes in * the descriptors of the marked class members. + * @param markCodeAttributes specified whether to mark the code attributes + * of the marked class methods. * @param allowShrinking specifies whether shrinking is allowed. * @param allowOptimization specifies whether optimization is allowed. * @param allowObfuscation specifies whether obfuscation is allowed. + * @param condition an optional extra condition. * @param classSpecification the specification of classes and class * members. */ public KeepClassSpecification(boolean markClasses, boolean markConditionally, boolean markDescriptorClasses, + boolean markCodeAttributes, boolean allowShrinking, boolean allowOptimization, boolean allowObfuscation, + ClassSpecification condition, ClassSpecification classSpecification) { super(classSpecification); @@ -98,14 +73,17 @@ public KeepClassSpecification(boolean markClasses, this.markClasses = markClasses; this.markConditionally = markConditionally; this.markDescriptorClasses = markDescriptorClasses; + this.markCodeAttributes = markCodeAttributes; this.allowShrinking = allowShrinking; this.allowOptimization = allowOptimization; this.allowObfuscation = allowObfuscation; + this.condition = condition; } // Implementations for Object. + @Override public boolean equals(Object object) { if (object == null || @@ -119,24 +97,32 @@ public boolean equals(Object object) this.markClasses == other.markClasses && this.markConditionally == other.markConditionally && this.markDescriptorClasses == other.markDescriptorClasses && + this.markCodeAttributes == other.markCodeAttributes && this.allowShrinking == other.allowShrinking && this.allowOptimization == other.allowOptimization && this.allowObfuscation == other.allowObfuscation && + (this.condition == null ? + other.condition == null : + this.condition .equals(other.condition)) && super.equals(other); } + @Override public int hashCode() { return - (markClasses ? 0 : 1) ^ - (markConditionally ? 0 : 2) ^ - (markDescriptorClasses ? 0 : 4) ^ - (allowShrinking ? 0 : 8) ^ - (allowOptimization ? 0 : 16) ^ - (allowObfuscation ? 0 : 32) ^ + (markClasses ? 0 : 1) ^ + (markConditionally ? 0 : 2) ^ + (markDescriptorClasses ? 0 : 4) ^ + (markCodeAttributes ? 0 : 8) ^ + (allowShrinking ? 0 : 16) ^ + (allowOptimization ? 0 : 32) ^ + (allowObfuscation ? 0 : 64) ^ + (condition == null ? 0 : condition.hashCode()) ^ super.hashCode(); } + @Override public Object clone() { // try diff --git a/core/src/proguard/KeepClassSpecificationVisitorFactory.java b/core/src/proguard/KeepClassSpecificationVisitorFactory.java new file mode 100644 index 000000000..72a14fb8a --- /dev/null +++ b/core/src/proguard/KeepClassSpecificationVisitorFactory.java @@ -0,0 +1,293 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.attribute.visitor.*; +import proguard.classfile.visitor.*; + +import java.util.*; + +/** + * This factory creates visitors to efficiently travel to specified classes and + * class members. + * + * @author Eric Lafortune + */ +public class KeepClassSpecificationVisitorFactory +extends ClassSpecificationVisitorFactory +{ + private final boolean shrinking; + private final boolean optimizing; + private final boolean obfuscating; + + + /** + * Creates a new KeepClassSpecificationVisitorFactory that creates + * visitors for the specified goal. + * + * @param shrinking a flag that specifies whether the visitors are + * intended for the shrinking step. + * @param optimizing a flag that specifies whether the visitors are + * intended for the optimization step. + * @param obfuscating a flag that specifies whether the visitors are + * intended for the obfuscation step. + */ + public KeepClassSpecificationVisitorFactory(boolean shrinking, + boolean optimizing, + boolean obfuscating) + { + this.shrinking = shrinking; + this.optimizing = optimizing; + this.obfuscating = obfuscating; + } + + + // Overriding implementations for ClassSpecificationVisitorFactory. + + /** + * Constructs a ClassPoolVisitor to efficiently travel to the specified + * classes, class members and code attributes. + * + * @param keepClassSpecifications the specifications of the class(es) and + * class members to visit. + * @param classVisitor an optional ClassVisitor to be applied to + * matching classes. + * @param fieldVisitor an optional MemberVisitor to be applied + * to matching fields. + * @param methodVisitor an optional MemberVisitor to be applied + * to matching methods. + * @param attributeVisitor an optional AttributeVisitor to be applied + * to matching code attributes. + */ + public ClassPoolVisitor createClassPoolVisitor(List keepClassSpecifications, + ClassVisitor classVisitor, + MemberVisitor fieldVisitor, + MemberVisitor methodVisitor, + AttributeVisitor attributeVisitor) + { + 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, + fieldVisitor, + methodVisitor, + attributeVisitor)); + } + } + } + + return multiClassPoolVisitor; + } + + + /** + * Constructs a ClassPoolVisitor to efficiently travel to the specified + * classes, class members, and attributes. + * + * @param keepClassSpecification the specifications of the class(es) and + * class members to visit. + * @param classVisitor an optional ClassVisitor to be applied to + * matching classes. + * @param fieldVisitor an optional MemberVisitor to be applied + * to matching fields. + * @param methodVisitor an optional MemberVisitor to be applied + * to matching methods. + * @param attributeVisitor an optional AttributeVisitor to be applied + * to matching code attributes. + */ + public ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification, + ClassVisitor classVisitor, + MemberVisitor fieldVisitor, + MemberVisitor methodVisitor, + AttributeVisitor attributeVisitor) + { + // Start a global list of wilcdard matchers, so they can be referenced + // from regular expressions. They are identified by their indices, + // which imposes a number of tricky constraints: + // - They need to be parsed in the right order, so the list is filled + // out in the expected order (corresponding to the text file + // configuration). + // - They need to be matched in the right order, so the variable + // matchers are matched before they are referenced. + List variableStringMatchers = new ArrayList(); + + // If specified, let the class visitor also visit the descriptor + // classes and the signature classes. + if (keepClassSpecification.markDescriptorClasses && + classVisitor != null) + { + fieldVisitor = fieldVisitor == null ? + new MemberDescriptorReferencedClassVisitor(classVisitor) : + new MultiMemberVisitor( + new MemberVisitor[] + { + fieldVisitor, + new MemberDescriptorReferencedClassVisitor(classVisitor) + }); + + methodVisitor = methodVisitor == null ? + new MemberDescriptorReferencedClassVisitor(classVisitor) : + new MultiMemberVisitor( + new MemberVisitor[] + { + methodVisitor, + new MemberDescriptorReferencedClassVisitor(classVisitor) + }); + } + + // Don't visit the classes if not specified. + if (!keepClassSpecification.markClasses && + !keepClassSpecification.markConditionally) + { + classVisitor = null; + } + + // Don't visit the code attributes if not specified. + attributeVisitor = keepClassSpecification.markCodeAttributes ? + new AttributeNameFilter(ClassConstants.ATTR_Code, attributeVisitor) : + null; + + ClassSpecification condition = keepClassSpecification.condition; + if (condition != null) + { + // Parse the condition. We need to parse it before the actual keep + // specification, to make sure the list of variable string matchers + // is filled out in the right order. + + // Create a placeholder for the class pool visitor that + // corresponds to the actual keep specification. Note that we + // visit the entire class pool for each matched class. + MultiClassPoolVisitor keepClassPoolVisitor = + new MultiClassPoolVisitor(); + + // Parse the condition. + ClassPoolVisitor conditionalKeepClassPoolVisitor = + createClassTester(condition, + keepClassPoolVisitor, + variableStringMatchers); + + // Parse the actual keep specification and add it to the + // placeholder. + keepClassPoolVisitor.addClassPoolVisitor( + createClassPoolVisitor(keepClassSpecification, + classVisitor, + fieldVisitor, + methodVisitor, + attributeVisitor, + variableStringMatchers)); + + return conditionalKeepClassPoolVisitor; + } + else + { + // Just parse the actual keep specification. + return createClassPoolVisitor(keepClassSpecification, + classVisitor, + fieldVisitor, + methodVisitor, + attributeVisitor, + variableStringMatchers); + } + } + + + /** + * 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 an optional ClassVisitor to be applied to + * matching classes. + * @param fieldVisitor an optional MemberVisitor to be applied + * to matching fields. + * @param methodVisitor an optional MemberVisitor to be applied + * to matching methods. + * @param attributeVisitor an optional AttributeVisitor to be applied + * to matching code attributes. + * @param variableStringMatchers a mutable list of VariableStringMatcher + * instances that match the wildcards. + */ + private ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification, + ClassVisitor classVisitor, + MemberVisitor fieldVisitor, + MemberVisitor methodVisitor, + AttributeVisitor attributeVisitor, + List variableStringMatchers) + { + // If specified, let the marker visit the class and its class + // members conditionally. + if (keepClassSpecification.markConditionally) + { + // Parse the condition. We need to parse it before the actual keep + // specification, to make sure the list of variable string matchers + // is filled out in the right order. + + // Create a placeholder for the class visitor that corresponds to + // the actual keep specification. + MultiClassVisitor keepClassVisitor = + new MultiClassVisitor(); + + // Parse the condition. Only add string matchers locally. + ClassPoolVisitor conditionalKeepClassPoolVisitor = + createClassTester(keepClassSpecification, + keepClassVisitor, + new ArrayList(variableStringMatchers)); + + // Parse the actual keep specification and add it to the + // placeholder. + keepClassVisitor.addClassVisitor( + createCombinedClassVisitor(keepClassSpecification.attributeNames, + keepClassSpecification.fieldSpecifications, + keepClassSpecification.methodSpecifications, + classVisitor, + fieldVisitor, + methodVisitor, + attributeVisitor, + variableStringMatchers)); + + return conditionalKeepClassPoolVisitor; + } + else + { + // Just parse the actual keep specification. + return super.createClassPoolVisitor(keepClassSpecification, + classVisitor, + fieldVisitor, + methodVisitor, + attributeVisitor, + variableStringMatchers); + } + } +} diff --git a/src/proguard/LibraryKeepChecker.java b/core/src/proguard/LibraryKeepChecker.java similarity index 79% rename from src/proguard/LibraryKeepChecker.java rename to core/src/proguard/LibraryKeepChecker.java index 1aa85ebd8..0f4072456 100644 --- a/src/proguard/LibraryKeepChecker.java +++ b/core/src/proguard/LibraryKeepChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,7 +23,6 @@ import proguard.classfile.*; import proguard.classfile.util.*; import proguard.classfile.visitor.*; -import proguard.optimize.*; import java.util.List; @@ -77,19 +76,30 @@ public void checkClassSpecifications(List keepSpecifications) keepName = keepClassSpecification.className; if (keepName != null) { + KeepClassSpecificationVisitorFactory visitorFactory = + new KeepClassSpecificationVisitorFactory(true, true, true); + // Doesn't the specification match any program classes? ClassCounter programClassCounter = new ClassCounter(); + programClassPool.accept( - ClassSpecificationVisitorFactory.createClassPoolVisitor(keepClassSpecification, - programClassCounter, - null)); + visitorFactory + .createClassPoolVisitor(keepClassSpecification, + programClassCounter, + null, + null, + null)); + if (programClassCounter.getCount() == 0) { // Print out notes about any matched library classes. libraryClassPool.accept( - ClassSpecificationVisitorFactory.createClassPoolVisitor(keepClassSpecification, - this, - null)); + visitorFactory + .createClassPoolVisitor(keepClassSpecification, + this, + null, + null, + null)); } } } diff --git a/src/proguard/LineWordReader.java b/core/src/proguard/LineWordReader.java similarity index 97% rename from src/proguard/LineWordReader.java rename to core/src/proguard/LineWordReader.java index 556feeb6c..48dc4e80d 100644 --- a/src/proguard/LineWordReader.java +++ b/core/src/proguard/LineWordReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/MemberSpecification.java b/core/src/proguard/MemberSpecification.java similarity index 95% rename from src/proguard/MemberSpecification.java rename to core/src/proguard/MemberSpecification.java index b5ed86bb8..17de67594 100644 --- a/src/proguard/MemberSpecification.java +++ b/core/src/proguard/MemberSpecification.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,6 +21,8 @@ package proguard; +import java.util.List; + /** * This class stores a specification of class members. The specification is * template-based: the class member names and descriptors can contain wildcards. @@ -29,11 +31,12 @@ */ public class MemberSpecification { - public int requiredSetAccessFlags; - public int requiredUnsetAccessFlags; + public int requiredSetAccessFlags; + public int requiredUnsetAccessFlags; public final String annotationType; public final String name; public final String descriptor; + public final List attributeNames = null; /** diff --git a/core/src/proguard/OutputWriter.java b/core/src/proguard/OutputWriter.java new file mode 100644 index 000000000..3f635c680 --- /dev/null +++ b/core/src/proguard/OutputWriter.java @@ -0,0 +1,298 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.ClassUtil; +import proguard.configuration.ConfigurationLogger; +import proguard.io.*; +import proguard.util.*; + +import java.io.*; +import java.nio.charset.Charset; +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, + MultiValueMap injectedClassNameMap) throws IOException + { + ClassPath programJars = configuration.programJars; + + // Create a data entry writer factory with common archival parameters. + DataEntryWriterFactory dataEntryWriterFactory = + new DataEntryWriterFactory(programClassPool, + injectedClassNameMap); + + 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()) + { + // It's an input entry. Remember the highest index. + lastInputIndex = index; + } + else + { + // It's an output entry. Is it the last one in a + // series of output entries? + int nextIndex = index + 1; + if (nextIndex == programJars.size() || + !programJars.get(nextIndex).isOutput()) + { + // Write the processed input entries to the output entries. + writeOutput(dataEntryWriterFactory, + 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(DataEntryWriterFactory dataEntryWriterFactory, + ClassPool programClassPool, + ClassPath classPath, + int fromInputIndex, + int fromOutputIndex, + int toOutputIndex) + throws IOException + { + try + { + // Construct the writer that can write apks, jars, wars, ears, zips, + // and directories, cascading over the specified output entries. + DataEntryWriter writer = + dataEntryWriterFactory.createDataEntryWriter(classPath, + fromOutputIndex, + toOutputIndex); + + if (configuration.addConfigurationDebugging) + { + writer = new ExtraDataEntryWriter(ConfigurationLogger.CLASS_MAP_FILENAME, + writer, + new ClassMapDataEntryWriter(programClassPool, writer)); + System.err.println("Warning: -addconfigurationdebugging is enabled; the resulting build will contain obfuscation information."); + System.err.println("It should only be used for debugging purposes."); + } + + DataEntryWriter resourceWriter = writer; + + // Adapt plain resource file names that correspond to class names, + // if necessary. + if (configuration.obfuscate && + configuration.adaptResourceFileNames != null) + { + // Rename processed general resources. + resourceWriter = + renameResourceFiles(programClassPool, + resourceWriter); + } + + // By default, just copy resource files into the above writers. + DataEntryReader resourceCopier = + new DataEntryCopier(resourceWriter); + + // We're now switching to the reader side, operating on the + // contents possibly parsed from the input streams. + DataEntryReader resourceRewriter = resourceCopier; + + // Adapt resource file contents, if allowed. + if ((configuration.shrink || + configuration.optimize || + configuration.obfuscate) && + configuration.adaptResourceFileContents != null) + { + DataEntryReader adaptingContentWriter = resourceRewriter; + + // Adapt the contents of general resource files (manifests + // and native libraries). + if (configuration.obfuscate) + { + adaptingContentWriter = + adaptResourceFiles(programClassPool, + resourceWriter); + } + + // Add the overall filter for adapting resource file contents. + resourceRewriter = + new NameFilter(configuration.adaptResourceFileContents, + adaptingContentWriter, + resourceRewriter); + } + + // Write any kept directories. + DataEntryReader reader = + writeDirectories(programClassPool, + resourceCopier, + resourceRewriter); + + // Trigger writing classes. + reader = + new ClassFilter(new IdleRewriter(writer), + reader); + + // 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); + } + } + + + /** + * Returns a writer that writes possibly renamed resource files to the + * given resource writer. + */ + private DataEntryWriter renameResourceFiles(ClassPool programClassPool, + DataEntryWriter dataEntryWriter) + { + Map packagePrefixMap = createPackagePrefixMap(programClassPool); + + return + new NameFilteredDataEntryWriter(configuration.adaptResourceFileNames, + new RenamedDataEntryWriter(programClassPool, packagePrefixMap, dataEntryWriter), + dataEntryWriter); + } + + + /** + * Returns a reader that writes all general resource files (manifest, + * native libraries, text files) with shrunk, optimized, and obfuscated + * contents to the given writer. + */ + private DataEntryReader adaptResourceFiles(ClassPool programClassPool, + DataEntryWriter writer) + { + // Pick a suitable encoding. + Charset charset = configuration.android ? + Charset.forName("UTF-8") : + Charset.defaultCharset(); + + // Filter between the various general resource files. + return + new NameFilter("META-INF/MANIFEST.MF,META-INF/*.SF", + new ManifestRewriter(programClassPool, charset, writer), + new DataEntryRewriter(programClassPool, charset, writer)); + } + + + /** + * Writes possibly renamed directories that should be preserved to the + * given resource copier, and non-directories to the given file copier. + */ + private DirectoryFilter writeDirectories(ClassPool programClassPool, + DataEntryReader directoryCopier, + DataEntryReader fileCopier) + { + DataEntryReader directoryRewriter = null; + + // Wrap the directory copier with a filter and a data entry renamer. + if (configuration.keepDirectories != null) + { + Map packagePrefixMap = createPackagePrefixMap(programClassPool); + + directoryRewriter = + new NameFilter(configuration.keepDirectories, + new RenamedDataEntryReader(packagePrefixMap, + directoryCopier, + directoryCopier)); + } + + // Filter on directories and files. + return new DirectoryFilter(directoryRewriter, fileCopier); + } + + + /** + * 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/core/src/proguard/ParseException.java similarity index 96% rename from src/proguard/ParseException.java rename to core/src/proguard/ParseException.java index d937b7c00..ae537eabe 100644 --- a/src/proguard/ParseException.java +++ b/core/src/proguard/ParseException.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/ProGuard.java b/core/src/proguard/ProGuard.java similarity index 71% rename from src/proguard/ProGuard.java rename to core/src/proguard/ProGuard.java index 73dce7323..e57c26530 100644 --- a/src/proguard/ProGuard.java +++ b/core/src/proguard/ProGuard.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,15 +20,19 @@ */ package proguard; +import proguard.backport.Backporter; import proguard.classfile.*; import proguard.classfile.attribute.visitor.AllAttributeVisitor; import proguard.classfile.editor.*; +import proguard.classfile.util.*; import proguard.classfile.visitor.*; +import proguard.configuration.ConfigurationLoggingAdder; import proguard.obfuscate.Obfuscator; import proguard.optimize.Optimizer; import proguard.optimize.peephole.LineNumberLinearizer; import proguard.preverify.*; import proguard.shrink.Shrinker; +import proguard.util.*; import java.io.*; @@ -39,12 +43,17 @@ */ public class ProGuard { - public static final String VERSION = "ProGuard, version 5.3.3"; + public static final String VERSION = "ProGuard, version 6.0"; private final Configuration configuration; private ClassPool programClassPool = new ClassPool(); private final ClassPool libraryClassPool = new ClassPool(); + // Map with class names as keys, and the names of all injected classes that are + // referenced from these key classes as values. + // All names are the original, non-obfuscated class names. + private final MultiValueMap injectedClassNameMap = new MultiValueMap(); + /** * Creates a new ProGuard object to process jars as specified by the given @@ -79,6 +88,11 @@ public void execute() throws IOException return; } + if (configuration.targetClassVersion != 0) + { + configuration.backport = true; + } + readInput(); if (configuration.shrink || @@ -93,14 +107,26 @@ public void execute() throws IOException configuration.shrink || configuration.optimize || configuration.obfuscate || - configuration.preverify) + configuration.preverify || + configuration.backport) { initialize(); } - if (configuration.targetClassVersion != 0) + if (configuration.obfuscate || + configuration.optimize) { - target(); + introducePrimitiveArrayConstants(); + } + + if (configuration.backport) + { + backport(); + } + + if (configuration.addConfigurationDebugging) + { + addConfigurationLogging(); } if (configuration.printSeeds != null) @@ -108,14 +134,15 @@ public void execute() throws IOException printSeeds(); } - if (configuration.shrink) + if (configuration.preverify || + configuration.android) { - shrink(); + inlineSubroutines(); } - if (configuration.preverify) + if (configuration.shrink) { - inlineSubroutines(); + shrink(); } if (configuration.optimize) @@ -124,7 +151,7 @@ public void execute() throws IOException optimizationPass < configuration.optimizationPasses; optimizationPass++) { - if (!optimize()) + if (!optimize(optimizationPass+1, configuration.optimizationPasses)) { // Stop optimizing if the code doesn't improve any further. break; @@ -140,10 +167,7 @@ public void execute() throws IOException shrink(); } } - } - if (configuration.optimize) - { linearizeLineNumbers(); } @@ -152,11 +176,22 @@ public void execute() throws IOException obfuscate(); } + if (configuration.optimize || + configuration.obfuscate) + { + expandPrimitiveArrayConstants(); + } + if (configuration.optimize) { trimLineNumbers(); } + if (configuration.targetClassVersion != 0) + { + target(); + } + if (configuration.preverify) { preverify(); @@ -189,17 +224,19 @@ private void printConfiguration() throws IOException { if (configuration.verbose) { - System.out.println("Printing configuration to [" + fileName(configuration.printConfiguration) + "]..."); + System.out.println("Printing configuration to [" + + PrintWriterUtil.fileName(configuration.printConfiguration) + + "]..."); } - PrintStream ps = createPrintStream(configuration.printConfiguration); + PrintWriter pw = PrintWriterUtil.createPrintWriterOut(configuration.printConfiguration); try { - new ConfigurationWriter(ps).write(configuration); + new ConfigurationWriter(pw).write(configuration); } finally { - closePrintStream(ps); + PrintWriterUtil.closePrintWriter(configuration.printConfiguration, pw); } } @@ -215,7 +252,8 @@ private void readInput() throws IOException } // Fill the program class pool and the library class pool. - new InputReader(configuration).execute(programClassPool, libraryClassPool); + new InputReader(configuration).execute(programClassPool, + libraryClassPool); } @@ -230,7 +268,50 @@ private void initialize() throws IOException System.out.println("Initializing..."); } - new Initializer(configuration).execute(programClassPool, libraryClassPool); + new Initializer(configuration).execute(programClassPool, + libraryClassPool); + } + + + /** + * Replaces primitive array initialization code by primitive array constants. + */ + private void introducePrimitiveArrayConstants() + { + programClassPool.classesAccept(new ArrayInitializationReplacer()); + } + + + /** + * Expands primitive array constants back to traditional primitive array + * initialization code. + */ + private void expandPrimitiveArrayConstants() + { + programClassPool.classesAccept(new PrimitiveArrayConstantReplacer()); + } + + + /** + * Backports java language features to the specified target version. + */ + private void backport() + { + new Backporter(configuration).execute(programClassPool, + libraryClassPool, + injectedClassNameMap); + } + + + /** + * Adds configuration logging code, providing suggestions on improving + * the ProGuard configuration. + */ + private void addConfigurationLogging() + { + new ConfigurationLoggingAdder(configuration).execute(programClassPool, + libraryClassPool, + injectedClassNameMap); } @@ -259,14 +340,16 @@ private void printSeeds() throws IOException System.out.println("Printing kept classes, fields, and methods..."); } - PrintStream ps = createPrintStream(configuration.printSeeds); + PrintWriter pw = PrintWriterUtil.createPrintWriterOut(configuration.printSeeds); try { - new SeedPrinter(ps).write(configuration, programClassPool, libraryClassPool); + new SeedPrinter(pw).write(configuration, + programClassPool, + libraryClassPool); } finally { - closePrintStream(ps); + PrintWriterUtil.closePrintWriter(configuration.printSeeds, pw); } } @@ -289,13 +372,14 @@ private void shrink() throws IOException // We'll print out the usage, if requested. if (configuration.printUsage != null) { - System.out.println("Printing usage to [" + fileName(configuration.printUsage) + "]..."); + System.out.println("Printing usage to [" + PrintWriterUtil.fileName(configuration.printUsage) + "]..."); } } // Perform the actual shrinking. programClassPool = - new Shrinker(configuration).execute(programClassPool, libraryClassPool); + new Shrinker(configuration).execute(programClassPool, + libraryClassPool); } @@ -317,15 +401,18 @@ private void inlineSubroutines() /** * Performs the optimization step. */ - private boolean optimize() throws IOException + private boolean optimize(int currentPass, + int maxPasses) throws IOException { if (configuration.verbose) { - System.out.println("Optimizing..."); + System.out.println("Optimizing (pass " + currentPass + "/" + maxPasses + ")..."); } // Perform the actual optimization. - return new Optimizer(configuration).execute(programClassPool, libraryClassPool); + return new Optimizer(configuration).execute(programClassPool, + libraryClassPool, + injectedClassNameMap); } @@ -341,18 +428,19 @@ private void obfuscate() throws IOException // We'll apply a mapping, if requested. if (configuration.applyMapping != null) { - System.out.println("Applying mapping [" + fileName(configuration.applyMapping) + "]"); + System.out.println("Applying mapping [" + PrintWriterUtil.fileName(configuration.applyMapping) + "]"); } // We'll print out the mapping, if requested. if (configuration.printMapping != null) { - System.out.println("Printing mapping to [" + fileName(configuration.printMapping) + "]..."); + System.out.println("Printing mapping to [" + PrintWriterUtil.fileName(configuration.printMapping) + "]..."); } } // Perform the actual obfuscation. - new Obfuscator(configuration).execute(programClassPool, libraryClassPool); + new Obfuscator(configuration).execute(programClassPool, + libraryClassPool); } @@ -424,7 +512,8 @@ private void writeOutput() throws IOException } // Write out the program class pool. - new OutputWriter(configuration).execute(programClassPool); + new OutputWriter(configuration).execute(programClassPool, + injectedClassNameMap); } @@ -435,72 +524,17 @@ private void dump() throws IOException { if (configuration.verbose) { - System.out.println("Printing classes to [" + fileName(configuration.dump) + "]..."); + System.out.println("Printing classes to [" + PrintWriterUtil.fileName(configuration.dump) + "]..."); } - PrintStream ps = createPrintStream(configuration.dump); + PrintWriter pw = PrintWriterUtil.createPrintWriterOut(configuration.dump); try { - programClassPool.classesAccept(new ClassPrinter(ps)); + programClassPool.classesAccept(new ClassPrinter(pw)); } 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(); - } + PrintWriterUtil.closePrintWriter(configuration.dump, pw); } } diff --git a/src/proguard/SeedPrinter.java b/core/src/proguard/SeedPrinter.java similarity index 75% rename from src/proguard/SeedPrinter.java rename to core/src/proguard/SeedPrinter.java index b6a04c4aa..04e326d0d 100644 --- a/src/proguard/SeedPrinter.java +++ b/core/src/proguard/SeedPrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -33,15 +33,15 @@ */ public class SeedPrinter { - private final PrintStream ps; + private final PrintWriter printWriter; /** - * Creates a new ConfigurationWriter for the given PrintStream. + * Creates a new ConfigurationWriter that prints to the given writer. */ - public SeedPrinter(PrintStream ps) throws IOException + public SeedPrinter(PrintWriter printWriter) throws IOException { - this.ps = ps; + this.printWriter = printWriter; } @@ -69,23 +69,23 @@ public void write(Configuration configuration, // optimization, or obfuscation. KeepMarker keepMarker = new KeepMarker(); ClassPoolVisitor classPoolvisitor = - ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep, - keepMarker, - keepMarker, - true, - true, - true); + new KeepClassSpecificationVisitorFactory(true, true, true) + .createClassPoolVisitor(configuration.keep, + keepMarker, + keepMarker, + keepMarker, + null); + // 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[] - { + SimpleClassPrinter printer = new SimpleClassPrinter(false, printWriter); + programClassPool.classesAcceptAlphabetically( + new MultiClassVisitor( new KeptClassFilter(printer), new AllMemberVisitor(new KeptMemberFilter(printer)) - })); + )); } } \ No newline at end of file diff --git a/src/proguard/SubclassedClassFilter.java b/core/src/proguard/SubclassedClassFilter.java similarity index 96% rename from src/proguard/SubclassedClassFilter.java rename to core/src/proguard/SubclassedClassFilter.java index 47d73bab3..7ec4e60a9 100644 --- a/src/proguard/SubclassedClassFilter.java +++ b/core/src/proguard/SubclassedClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/Targeter.java b/core/src/proguard/Targeter.java similarity index 98% rename from src/proguard/Targeter.java rename to core/src/proguard/Targeter.java index 4618d5dc8..e81742d02 100644 --- a/src/proguard/Targeter.java +++ b/core/src/proguard/Targeter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/UpToDateChecker.java b/core/src/proguard/UpToDateChecker.java similarity index 92% rename from src/proguard/UpToDateChecker.java rename to core/src/proguard/UpToDateChecker.java index 97c4aea95..57351b2c9 100644 --- a/src/proguard/UpToDateChecker.java +++ b/core/src/proguard/UpToDateChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,6 +21,7 @@ package proguard; import java.io.File; +import java.net.*; /** * This class checks whether the output is up to date. @@ -118,6 +119,24 @@ private static class ModificationTimeChecker { private long outputModificationTime = Long.MAX_VALUE; + /** + * Updates the input modification time based on the given file or + * directory (recursively). + */ + public void updateInputModificationTime(URL url) + { + if (url != null && + url.getProtocol().equals("file")) + { + try + { + updateModificationTime(new File(url.toURI()), false); + } + catch (URISyntaxException ignore) {} + } + } + + /** * Updates the input modification time based on the given file or * directory (recursively). diff --git a/src/proguard/WordReader.java b/core/src/proguard/WordReader.java similarity index 85% rename from src/proguard/WordReader.java rename to core/src/proguard/WordReader.java index cc7cebd86..2af8e342c 100644 --- a/src/proguard/WordReader.java +++ b/core/src/proguard/WordReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,6 +21,7 @@ package proguard; import java.io.*; +import java.net.URL; /** @@ -38,6 +39,7 @@ public abstract class WordReader private File baseDir; + private URL baseURL; private WordReader includeWordReader; private String currentLine; private int currentLineLength; @@ -55,6 +57,15 @@ protected WordReader(File baseDir) } + /** + * Creates a new WordReader with the given base URL. + */ + protected WordReader(URL baseURL) + { + this.baseURL = baseURL; + } + + /** * Sets the base directory of this reader. */ @@ -82,6 +93,17 @@ public File getBaseDir() } + /** + * Returns the base URL of this reader, if any. + */ + public URL getBaseURL() + { + return includeWordReader != null ? + includeWordReader.getBaseURL() : + baseURL; + } + + /** * Specifies to start reading words from the given WordReader. When it is * exhausted, this WordReader will continue to provide its own words. @@ -105,11 +127,16 @@ public void includeWordReader(WordReader 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 '-'). + * @param isFileName return a complete line (or argument), if the word + * isn't an option (it doesn't start with '-'). + * @param expectSingleFile if true, the remaining line is expected to be a + * single file name (excluding path separator), + * otherwise multiple files might be specified + * using the path separator. * @return the read word. */ - public String nextWord(boolean isFileName) throws IOException + public String nextWord(boolean isFileName, + boolean expectSingleFile) throws IOException { currentWord = null; @@ -117,7 +144,7 @@ public String nextWord(boolean isFileName) throws IOException if (includeWordReader != null) { // Does the included word reader still produce a word? - currentWord = includeWordReader.nextWord(isFileName); + currentWord = includeWordReader.nextWord(isFileName, expectSingleFile); if (currentWord != null) { // Return it if so. @@ -220,7 +247,7 @@ else if (isFileName && while (currentIndex < currentLineLength) { char currentCharacter = currentLine.charAt(currentIndex); - if (isFileDelimiter(currentCharacter) || + if (isFileDelimiter(currentCharacter, !expectSingleFile) || ((isOption(currentCharacter) || isComment(currentCharacter)) && Character.isWhitespace(currentLine.charAt(currentIndex-1)))) { @@ -252,7 +279,7 @@ else if (isDelimiter(startChar)) while (currentIndex < currentLineLength) { char currentCharacter = currentLine.charAt(currentIndex); - if (isDelimiter(currentCharacter) || + if (isNonStartDelimiter(currentCharacter) || Character.isWhitespace(currentCharacter) || isComment(currentCharacter)) { break; @@ -358,8 +385,19 @@ private boolean isComment(char character) private boolean isDelimiter(char character) { - return character == '@' || - character == '{' || + return isStartDelimiter(character) || isNonStartDelimiter(character); + } + + + private boolean isStartDelimiter(char character) + { + return character == '@'; + } + + + private boolean isNonStartDelimiter(char character) + { + return character == '{' || character == '}' || character == '(' || character == ')' || @@ -369,13 +407,15 @@ private boolean isDelimiter(char character) } - private boolean isFileDelimiter(char character) + private boolean isFileDelimiter(char character, + boolean includePathSeparator) { return character == '(' || character == ')' || character == ',' || character == ';' || - character == File.pathSeparatorChar; + (includePathSeparator && + character == File.pathSeparatorChar); } diff --git a/core/src/proguard/backport/Backporter.java b/core/src/proguard/backport/Backporter.java new file mode 100644 index 000000000..6c3ae9c44 --- /dev/null +++ b/core/src/proguard/backport/Backporter.java @@ -0,0 +1,231 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.backport; + +import proguard.*; +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.Constant; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.instruction.visitor.InstructionCounter; +import proguard.classfile.util.ClassReferenceInitializer; +import proguard.classfile.visitor.*; +import proguard.optimize.peephole.*; +import proguard.util.MultiValueMap; + +/** + * This class backports classes to the specified targetClassVersion. + * + * @author Thomas Neidhart + */ +public class Backporter +{ + private final Configuration configuration; + + + public Backporter(Configuration configuration) + { + this.configuration = configuration; + } + + + public void execute(ClassPool programClassPool, + ClassPool libraryClassPool, + MultiValueMap injectedClassNameMap) + { + int targetClassVersion = configuration.targetClassVersion; + + if (configuration.verbose) + { + System.out.println("Backporting class files..."); + } + + // Clean up any previous visitor info. + programClassPool.classesAccept(new ClassCleaner()); + libraryClassPool.classesAccept(new ClassCleaner()); + + final InstructionCounter replacedStringConcatCounter = new InstructionCounter(); + final ClassCounter lambdaExpressionCounter = new ClassCounter(); + final MemberCounter staticInterfaceMethodCounter = new MemberCounter(); + final MemberCounter defaultInterfaceMethodCounter = new MemberCounter(); + final InstructionCounter replacedMethodCallCounter = new InstructionCounter(); + + if (targetClassVersion < ClassConstants.CLASS_VERSION_1_9) + { + // Convert indy string concatenations to StringBuilder chains + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true); + programClassPool.classesAccept( + new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_9, + new AllAttributeVisitor( + new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods, + new AttributeToClassVisitor( + new MultiClassVisitor( + new AllMethodVisitor( + new AllAttributeVisitor( + new PeepholeOptimizer(codeAttributeEditor, + + // Replace the indy instructions related to String concatenation. + new StringConcatenationConverter(replacedStringConcatCounter, + codeAttributeEditor))) + ), + + // Clean up unused bootstrap methods and their dangling constants. + new BootstrapMethodsAttributeShrinker(), + + // Initialize new references to StringBuilder. + new ClassReferenceInitializer(programClassPool, libraryClassPool) + )))))); + } + + if (targetClassVersion < ClassConstants.CLASS_VERSION_1_8) + { + // Collect all classes with BootstrapMethod attributes, + // and convert lambda expressions and method references. + ClassPool filteredClasses = new ClassPool(); + programClassPool.classesAccept( + new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_8, + new AllAttributeVisitor( + new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods, + new AttributeToClassVisitor( + new ClassPoolFiller(filteredClasses)))))); + + // Note: we visit the filtered classes in a separate step + // because we modify the programClassPool while converting + filteredClasses.classesAccept( + new MultiClassVisitor( + // Replace the indy instructions related to lambda expressions. + new LambdaExpressionConverter(programClassPool, + libraryClassPool, + injectedClassNameMap, + lambdaExpressionCounter), + + // Clean up unused bootstrap methods and their dangling constants. + new BootstrapMethodsAttributeShrinker(), + + // Re-initialize references. + new ClassReferenceInitializer(programClassPool, libraryClassPool) + )); + + // Remove static and default methods from interfaces if the + // target version is < 1.8. The dalvik format 037 has native + // support for default methods. The dalvik format specification + // does not explicitly mention static interface methods, although + // they seem to work correctly. + ClassPool interfaceClasses = new ClassPool(); + programClassPool.classesAccept( + new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_8, + new ClassAccessFilter(ClassConstants.ACC_INTERFACE, 0, + new ClassPoolFiller(interfaceClasses)))); + + ClassPool modifiedClasses = new ClassPool(); + ClassVisitor modifiedClassCollector = + new ClassPoolFiller(modifiedClasses); + + interfaceClasses.classesAccept( + new MultiClassVisitor( + new StaticInterfaceMethodConverter(programClassPool, + libraryClassPool, + injectedClassNameMap, + modifiedClassCollector, + staticInterfaceMethodCounter), + + new DefaultInterfaceMethodConverter(modifiedClassCollector, + defaultInterfaceMethodCounter) + )); + + // Re-Initialize references in modified classes. + modifiedClasses.classesAccept( + new ClassReferenceInitializer(programClassPool, + libraryClassPool)); + } + + if (targetClassVersion < ClassConstants.CLASS_VERSION_1_7) + { + // Replace / remove method calls only available in Java 7+. + InstructionSequenceBuilder ____ = + new InstructionSequenceBuilder(programClassPool, + libraryClassPool); + + Instruction[][][] instructions = new Instruction[][][] + { + // Replace Objects.requireNonNull(...) with Object.getClass(). + + // Starting in JDK 9, javac uses {@code requireNonNull} for + // synthetic null-checks + // (see + // JDK-8074306). + { + ____.invokestatic("java/util/Objects", + "requireNonNull", + "(Ljava/lang/Object;)Ljava/lang/Object;").__(), + + ____.dup() + .invokevirtual(ClassConstants.NAME_JAVA_LANG_OBJECT, + ClassConstants.METHOD_NAME_OBJECT_GET_CLASS, + ClassConstants.METHOD_TYPE_OBJECT_GET_CLASS) + .pop().__() + }, + + // Remove Throwable.addSuppressed(...). + { + ____.invokevirtual("java/util/Throwable", + "addSuppressed", + "(Ljava/lang/Throwable;)V").__(), + + ____.pop() // the suppressed exception + .pop().__() // the original exception + } + }; + + Constant[] constants = ____.constants(); + + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new PeepholeOptimizer(null, codeAttributeEditor, + new InstructionSequencesReplacer(constants, + instructions, + null, + codeAttributeEditor, + replacedMethodCallCounter))))); + } + + if (targetClassVersion != 0) + { + // Set the class version of all classes in the program ClassPool + // to the specified target version. This is needed to perform + // optimization on the backported + generated classes. + programClassPool.classesAccept(new ClassVersionSetter(targetClassVersion)); + } + + if (configuration.verbose) + { + System.out.println(" Number of converted string concatenations: " + replacedStringConcatCounter.getCount()); + System.out.println(" Number of converted lambda expressions: " + lambdaExpressionCounter.getCount()); + System.out.println(" Number of converted static interface methods: " + staticInterfaceMethodCounter.getCount()); + System.out.println(" Number of converted default interface methods: " + defaultInterfaceMethodCounter.getCount()); + System.out.println(" Number of replaced Java 7+ method calls: " + replacedMethodCallCounter.getCount()); + } + } +} diff --git a/core/src/proguard/backport/DefaultInterfaceMethodConverter.java b/core/src/proguard/backport/DefaultInterfaceMethodConverter.java new file mode 100644 index 000000000..6cd5ff741 --- /dev/null +++ b/core/src/proguard/backport/DefaultInterfaceMethodConverter.java @@ -0,0 +1,304 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.backport; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.*; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.optimize.peephole.*; +import proguard.util.StringTransformer; + +import java.util.*; + +/** + * This ClassVisitor moves all default interface methods in the visited + * interfaces to concrete implementations. + * + * @author Thomas Neidhart + */ +public class DefaultInterfaceMethodConverter +extends SimplifiedVisitor +implements ClassVisitor, + + // Implementation interfaces. + AttributeVisitor +{ + private final ClassVisitor modifiedClassVisitor; + private final MemberVisitor extraMemberVisitor; + + // Fields acting as parameters and return values for the visitor methods. + + private final Set implClasses = new LinkedHashSet(); + private boolean hasDefaultMethods; + + + public DefaultInterfaceMethodConverter(ClassVisitor modifiedClassVisitor, + MemberVisitor extraMemberVisitor) + { + this.modifiedClassVisitor = modifiedClassVisitor; + this.extraMemberVisitor = extraMemberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + hasDefaultMethods = false; + implClasses.clear(); + + // Collect all implementations of the interface. + programClass.hierarchyAccept(false, false, false, true, + new ProgramClassFilter( + new ClassCollector(implClasses))); + + programClass.accept( + new AllMethodVisitor( + new MemberAccessFilter(0, ClassConstants.ACC_STATIC, + new AllAttributeVisitor(this)))); + + if (hasDefaultMethods) + { + // Shrink the constant pool of unused constants. + programClass.accept(new ConstantPoolShrinker()); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + hasDefaultMethods = true; + + ProgramClass interfaceClass = (ProgramClass) clazz; + ProgramMethod defaultMethod = (ProgramMethod) method; + + for (Clazz implClass : implClasses) + { + ProgramClass targetClass = (ProgramClass) implClass; + + // Add the default method to the implementing class + // if necessary. + if (!hasInheritedMethod(targetClass, + defaultMethod.getName(interfaceClass), + defaultMethod.getDescriptor(interfaceClass))) + { + defaultMethod.accept(interfaceClass, + new MemberAdder(targetClass)); + + targetClass.accept(modifiedClassVisitor); + } + + // Add the default method as a different method and adapt + // super invocations to it, if necessary. + if (callsDefaultMethodUsingSuper(targetClass, + interfaceClass, + defaultMethod)) + { + replaceDefaultMethodInvocation(targetClass, + interfaceClass, + defaultMethod); + + targetClass.accept(modifiedClassVisitor); + } + } + + // Remove the code attribute from the method and + // add make it abstract. + defaultMethod.accept(interfaceClass, + new MultiMemberVisitor( + new NamedAttributeDeleter(ClassConstants.ATTR_Code), + + new MemberAccessFlagSetter(ClassConstants.ACC_ABSTRACT) + )); + + // Call extra visitor for each visited default method. + if (extraMemberVisitor != null) + { + defaultMethod.accept(interfaceClass, extraMemberVisitor); + } + } + + + // Small utility methods. + + private boolean hasInheritedMethod(Clazz clazz, + String methodName, + String methodDescriptor) + { + MemberCounter counter = new MemberCounter(); + + clazz.hierarchyAccept(true, true, false, false, + new NamedMethodVisitor(methodName, methodDescriptor, + counter)); + + return counter.getCount() > 0; + } + + + /** + * Returns true if any method of the given class + * calls Interface.super.defaultMethod(...). + */ + private boolean callsDefaultMethodUsingSuper(Clazz clazz, + Clazz interfaceClass, + Method defaultMethod) + { + ConstantCounter counter = new ConstantCounter(); + + clazz.accept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new InvocationInstructionMatcher(interfaceClass, + defaultMethod, + counter))))); + + return counter.getCount() > 0; + } + + + /** + * Replaces any super calls to the given default interface method + * in the target class. The default method is copied to the target + * class and the invoke is updated accordingly. + */ + private void replaceDefaultMethodInvocation(ProgramClass targetClass, + ProgramClass interfaceClass, + ProgramMethod interfaceMethod) + { + // Copy the interface method to the target class, with an updated name. + StringTransformer memberRenamer = new StringTransformer() + { + public String transform(String string) + { + return "default$" + string; + } + }; + + interfaceMethod.accept(interfaceClass, + new MemberAdder(targetClass, memberRenamer, null)); + + String targetMethodName = + memberRenamer.transform(interfaceMethod.getName(interfaceClass)); + + // Update invocations of the method inside the target class. + String descriptor = interfaceMethod.getDescriptor(interfaceClass); + Method targetMethod = targetClass.findMethod(targetMethodName, descriptor); + + InstructionSequenceBuilder ____ = + new InstructionSequenceBuilder(); + + Instruction[] patternInstructions = + ____.invokespecial_interface(interfaceClass, + interfaceMethod).__(); + + Instruction[] replacementInstructions = + ____.invokevirtual(targetClass, + targetMethod).__(); + + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + Constant[] constants = ____.constants(); + + targetClass.accept( + new AllMethodVisitor( + new AllAttributeVisitor( + new PeepholeOptimizer(null, codeAttributeEditor, + new InstructionSequenceReplacer(constants, + patternInstructions, + constants, + replacementInstructions, + null, + codeAttributeEditor))))); + } + + + /** + * This InstructionVisitor will call the specified ConstantVisitor + * for any encountered INVOKESPECIAL instruction whose associated + * constant is an InterfaceMethodRefConstant and matches the given + * referenced class and method. + */ + private static class InvocationInstructionMatcher + extends SimplifiedVisitor + implements InstructionVisitor, + ConstantVisitor + { + private final Clazz referencedClass; + private final Method referencedMethod; + private final ConstantVisitor constantVisitor; + + public InvocationInstructionMatcher(Clazz referencedClass, + Method referencedMethod, + ConstantVisitor constantVisitor) + { + this.referencedClass = referencedClass; + this.referencedMethod = referencedMethod; + 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) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_INVOKESPECIAL: + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + if (interfaceMethodrefConstant.referencedClass == referencedClass && + interfaceMethodrefConstant.referencedMember == referencedMethod) + { + constantVisitor.visitInterfaceMethodrefConstant(clazz, interfaceMethodrefConstant); + } + } + } +} diff --git a/core/src/proguard/backport/LambdaExpression.java b/core/src/proguard/backport/LambdaExpression.java new file mode 100644 index 000000000..88f97d95e --- /dev/null +++ b/core/src/proguard/backport/LambdaExpression.java @@ -0,0 +1,192 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + */ +package proguard.backport; + +import proguard.classfile.ClassConstants; +import proguard.classfile.Clazz; +import proguard.classfile.Method; +import proguard.classfile.ProgramClass; +import proguard.classfile.attribute.BootstrapMethodInfo; +import proguard.classfile.util.ClassUtil; +import proguard.classfile.util.MemberFinder; + +/** + * A small helper class that captures useful information + * about a lambda expression as encountered in a class file. + * + * @author Thomas Neidhart + */ +public class LambdaExpression +{ + // The referenced class of the lambda expression. + public ProgramClass referencedClass; + + // The referenced bootstrap method index. + public int bootstrapMethodIndex; + // The referenced bootstrap method info. + public BootstrapMethodInfo bootstrapMethodInfo; + + // The lambda factory method type. + public String factoryMethodDescriptor; + + // The implemented interfaces of the Lambda expression. + public String[] interfaces; + + // The additional bridge method descriptors to be added. + public String[] bridgeMethodDescriptors; + + // The name and descriptor of the implemented interface method. + public String interfaceMethod; + public String interfaceMethodDescriptor; + + // Information regarding the invoked method. + public int invokedReferenceKind; + public String invokedClassName; + public String invokedMethodName; + public String invokedMethodDesc; + + public Clazz referencedInvokedClass; + public Method referencedInvokedMethod; + + // The created lambda class. + public ProgramClass lambdaClass; + + + /** + * Creates a new initialized LambdaExpression (except for the lambdaClass). + */ + public LambdaExpression(ProgramClass referencedClass, + int bootstrapMethodIndex, + BootstrapMethodInfo bootstrapMethodInfo, + String factoryMethodDescriptor, + String[] interfaces, + String[] bridgeMethodDescriptors, + String interfaceMethod, + String interfaceMethodDescriptor, + int invokedReferenceKind, + String invokedClassName, + String invokedMethodName, + String invokedMethodDesc, + Clazz referencedInvokedClass, + Method referencedInvokedMethod) + { + this.referencedClass = referencedClass; + this.bootstrapMethodIndex = bootstrapMethodIndex; + this.bootstrapMethodInfo = bootstrapMethodInfo; + this.factoryMethodDescriptor = factoryMethodDescriptor; + this.interfaces = interfaces; + this.bridgeMethodDescriptors = bridgeMethodDescriptors; + this.interfaceMethod = interfaceMethod; + this.interfaceMethodDescriptor = interfaceMethodDescriptor; + this.invokedReferenceKind = invokedReferenceKind; + this.invokedClassName = invokedClassName; + this.invokedMethodName = invokedMethodName; + this.invokedMethodDesc = invokedMethodDesc; + this.referencedInvokedClass = referencedInvokedClass; + this.referencedInvokedMethod = referencedInvokedMethod; + } + + + /** + * Returns the class name of the converted anonymous class. + */ + public String getLambdaClassName() + { + return String.format("%s$$Lambda$%d", + referencedClass.getName(), + bootstrapMethodIndex); + } + + + public String getConstructorDescriptor() + { + if (isStateless()) + { + return ClassConstants.METHOD_TYPE_INIT; + } + else + { + int endIndex = factoryMethodDescriptor.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE); + + return factoryMethodDescriptor.substring(0, endIndex + 1) + ClassConstants.TYPE_VOID; + } + } + + + /** + * Returns whether the lambda expression is serializable. + */ + public boolean isSerializable() + { + for (String interfaceName : interfaces) + { + if (ClassConstants.NAME_JAVA_IO_SERIALIZABLE.equals(interfaceName)) + { + return true; + } + } + return false; + } + + + /** + * Returns whether the lambda expression is actually a method reference. + */ + public boolean isMethodReference() + { + return !isLambdaMethod(invokedMethodName); + } + + + /** + * Returns whether the lambda expression is stateless. + */ + public boolean isStateless() + { + // The lambda expression is stateless if the factory method does + // not have arguments. + return + ClassUtil.internalMethodParameterCount(factoryMethodDescriptor) == 0; + } + + + /** + * Returns whether the invoked method is a static interface method. + */ + public boolean invokesStaticInterfaceMethod() + { + // We assume unknown classes are not interfaces. + return invokedReferenceKind == ClassConstants.REF_invokeStatic && + referencedInvokedClass != null && + (referencedInvokedClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) != 0; + } + + + /** + * Returns whether an accessor method is needed to access + * the invoked method from the lambda class. + */ + public boolean needsAccessorMethod() + { + // We assume unknown classes don't need an accessor method. + return referencedInvokedClass != null && + new MemberFinder().findMethod(lambdaClass, + referencedInvokedClass, + invokedMethodName, + invokedMethodDesc) == null; + } + + + // Small Utility methods. + + private static final String LAMBDA_METHOD_PREFIX = "lambda$"; + + private static boolean isLambdaMethod(String methodName) + { + return methodName.startsWith(LAMBDA_METHOD_PREFIX); + } +} diff --git a/core/src/proguard/backport/LambdaExpressionCollector.java b/core/src/proguard/backport/LambdaExpressionCollector.java new file mode 100644 index 000000000..bea115e6c --- /dev/null +++ b/core/src/proguard/backport/LambdaExpressionCollector.java @@ -0,0 +1,235 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.backport; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.ClassVisitor; +import proguard.util.ArrayUtil; + +import java.util.*; + +/** + * This ClassVisitor collects all lambda expressions that are defined in + * a visited class. + * + * @author Thomas Neidhart + */ +public class LambdaExpressionCollector +extends SimplifiedVisitor +implements ClassVisitor, + + // Implementation interfaces. + ConstantVisitor, + AttributeVisitor, + BootstrapMethodInfoVisitor +{ + private final Map lambdaExpressions; + + private InvokeDynamicConstant referencedInvokeDynamicConstant; + private int referencedBootstrapMethodIndex; + private Clazz referencedInvokedClass; + private Method referencedInvokedMethod; + + + public LambdaExpressionCollector(Map lambdaExpressions) + { + this.lambdaExpressions = lambdaExpressions; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + // Visit any InvokeDynamic constant. + programClass.constantPoolEntriesAccept( + new ConstantTagFilter(ClassConstants.CONSTANT_InvokeDynamic, + this)); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + referencedInvokeDynamicConstant = invokeDynamicConstant; + referencedBootstrapMethodIndex = invokeDynamicConstant.getBootstrapMethodAttributeIndex(); + clazz.attributesAccept(this); + } + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + referencedInvokedClass = refConstant.referencedClass; + referencedInvokedMethod = (Method) refConstant.referencedMember; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitBootstrapMethodsAttribute(Clazz clazz, + BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + bootstrapMethodsAttribute.bootstrapMethodEntryAccept(clazz, referencedBootstrapMethodIndex, this); + } + + + // Implementations for BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + ProgramClass programClass = (ProgramClass) clazz; + + MethodHandleConstant bootstrapMethodHandle = + (MethodHandleConstant) programClass.getConstant(bootstrapMethodInfo.u2methodHandleIndex); + + if (isLambdaMetaFactory(bootstrapMethodHandle.getClassName(clazz))) + { + String factoryMethodDescriptor = + referencedInvokeDynamicConstant.getType(clazz); + + String interfaceClassName = + ClassUtil.internalClassNameFromClassType(ClassUtil.internalMethodReturnType(factoryMethodDescriptor)); + + // Find the actual method that is being invoked. + MethodHandleConstant invokedMethodHandle = + (MethodHandleConstant) programClass.getConstant(bootstrapMethodInfo.u2methodArguments[1]); + + referencedInvokedClass = null; + referencedInvokedMethod = null; + clazz.constantPoolEntryAccept(invokedMethodHandle.u2referenceIndex, this); + + // Collect all the useful information. + LambdaExpression lambdaExpression = + new LambdaExpression(programClass, + referencedBootstrapMethodIndex, + bootstrapMethodInfo, + factoryMethodDescriptor, + new String[] { interfaceClassName }, + new String[0], + referencedInvokeDynamicConstant.getName(clazz), + getMethodTypeConstant(programClass, bootstrapMethodInfo.u2methodArguments[0]).getType(clazz), + invokedMethodHandle.getReferenceKind(), + invokedMethodHandle.getClassName(clazz), + invokedMethodHandle.getName(clazz), + invokedMethodHandle.getType(clazz), + referencedInvokedClass, + referencedInvokedMethod); + + if (isAlternateFactoryMethod(bootstrapMethodHandle.getName(clazz))) + { + int flags = + getIntegerConstant(programClass, + bootstrapMethodInfo.u2methodArguments[3]); + + // For the alternate metafactory, the optional arguments start + // at index 4. + int argumentIndex = 4; + + if ((flags & ClassConstants.FLAG_MARKERS) != 0) + { + int markerInterfaceCount = + getIntegerConstant(programClass, + bootstrapMethodInfo.u2methodArguments[argumentIndex++]); + + for (int i = 0; i < markerInterfaceCount; i++) + { + String interfaceName = + programClass.getClassName(bootstrapMethodInfo.u2methodArguments[argumentIndex++]); + + lambdaExpression.interfaces = + ArrayUtil.add(lambdaExpression.interfaces, + lambdaExpression.interfaces.length, + interfaceName); + } + } + + if ((flags & ClassConstants.FLAG_BRIDGES) != 0) + { + int bridgeMethodCount = + getIntegerConstant(programClass, + bootstrapMethodInfo.u2methodArguments[argumentIndex++]); + + for (int i = 0; i < bridgeMethodCount; i++) + { + MethodTypeConstant methodTypeConstant = + (MethodTypeConstant) programClass.getConstant(argumentIndex++); + + lambdaExpression.bridgeMethodDescriptors = + ArrayUtil.add(lambdaExpression.bridgeMethodDescriptors, + lambdaExpression.bridgeMethodDescriptors.length, + methodTypeConstant.getType(programClass)); + } + } + + if ((flags & ClassConstants.FLAG_SERIALIZABLE) != 0) + { + lambdaExpression.interfaces = + ArrayUtil.add(lambdaExpression.interfaces, + lambdaExpression.interfaces.length, + ClassConstants.NAME_JAVA_IO_SERIALIZABLE); + } + } + + lambdaExpressions.put(referencedBootstrapMethodIndex, lambdaExpression); + } + } + + // Small utility methods + + private static final String NAME_JAVA_LANG_INVOKE_LAMBDA_METAFACTORY = "java/lang/invoke/LambdaMetafactory"; + private static final String LAMBDA_ALTERNATE_METAFACTORY_METHOD = "altMetafactory"; + + private static boolean isLambdaMetaFactory(String className) + { + return NAME_JAVA_LANG_INVOKE_LAMBDA_METAFACTORY.equals(className); + } + + private static boolean isAlternateFactoryMethod(String methodName) + { + return LAMBDA_ALTERNATE_METAFACTORY_METHOD.equals(methodName); + } + + private static int getIntegerConstant(ProgramClass programClass, int constantIndex) + { + return ((IntegerConstant) programClass.getConstant(constantIndex)).getValue(); + } + + + private static MethodTypeConstant getMethodTypeConstant(ProgramClass programClass, int constantIndex) + { + return (MethodTypeConstant) programClass.getConstant(constantIndex); + } +} diff --git a/core/src/proguard/backport/LambdaExpressionConverter.java b/core/src/proguard/backport/LambdaExpressionConverter.java new file mode 100644 index 000000000..ca68a9a4a --- /dev/null +++ b/core/src/proguard/backport/LambdaExpressionConverter.java @@ -0,0 +1,991 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.backport; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.util.*; + +import java.util.*; + +/** + * This ClassVisitor converts all lambda expressions in the visited + * classes to anonymous inner classes. + * + * @author Thomas Neidhart + */ +public class LambdaExpressionConverter +extends SimplifiedVisitor +implements ClassVisitor, + + // Implementation interfaces. + MemberVisitor, + AttributeVisitor, + InstructionVisitor +{ + private static final String LAMBDA_SINGLETON_FIELD_NAME = "INSTANCE"; + + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final MultiValueMap injectedClassNameMap; + private final ClassVisitor extraClassVisitor; + + private final Map lambdaExpressionMap; + private final CodeAttributeEditor codeAttributeEditor; + private final MemberRemover memberRemover; + + + public LambdaExpressionConverter(ClassPool programClassPool, + ClassPool libraryClassPool, + MultiValueMap injectedClassNameMap, + ClassVisitor extraClassVisitor) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.injectedClassNameMap = injectedClassNameMap; + this.extraClassVisitor = extraClassVisitor; + + this.lambdaExpressionMap = new HashMap(); + this.codeAttributeEditor = new CodeAttributeEditor(true, true); + this.memberRemover = new MemberRemover(); + } + + + // Implementations for ClassVisitor. + + @Override + public void visitLibraryClass(LibraryClass libraryClass) {} + + + @Override + public void visitProgramClass(ProgramClass programClass) + { + lambdaExpressionMap.clear(); + programClass.accept(new LambdaExpressionCollector(lambdaExpressionMap)); + + for (LambdaExpression lambdaExpression : lambdaExpressionMap.values()) + { + ProgramClass lambdaClass = createLambdaClass(lambdaExpression); + + // Add the converted lambda class to the program class pool + // and the injected class name map. + programClassPool.addClass(lambdaClass); + injectedClassNameMap.put(programClass.getName(), lambdaClass.getName()); + + if (extraClassVisitor != null) + { + extraClassVisitor.visitProgramClass(lambdaClass); + } + } + + if (!lambdaExpressionMap.isEmpty()) + { + // Replace all InvokeDynamic instructions. + programClass.accept( + new AllMethodVisitor( + new AllAttributeVisitor( + this))); + + // Initialize the hierarchy and references of all lambda classes. + for (LambdaExpression lambdaExpression : lambdaExpressionMap.values()) + { + lambdaExpression.lambdaClass.accept( + new MultiClassVisitor( + new ClassSuperHierarchyInitializer(programClassPool, libraryClassPool), + new ClassSubHierarchyInitializer(), + new ClassReferenceInitializer(programClassPool, libraryClassPool) + )); + } + + // Remove deserialization hooks as they are no longer needed. + programClass.methodsAccept(this); + memberRemover.visitProgramClass(programClass); + } + } + + + // Implementations for AttributeVisitor. + + @Override + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + @Override + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + codeAttribute.instructionsAccept(clazz, method, this); + + if (codeAttributeEditor.isModified()) + { + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + } + + + // Implementations for InstructionVisitor. + + @Override + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + @Override + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + if (constantInstruction.opcode == InstructionConstants.OP_INVOKEDYNAMIC) + { + ProgramClass programClass = (ProgramClass) clazz; + + InvokeDynamicConstant invokeDynamicConstant = + (InvokeDynamicConstant) programClass.getConstant(constantInstruction.constantIndex); + + int bootstrapMethodIndex = invokeDynamicConstant.getBootstrapMethodAttributeIndex(); + if (lambdaExpressionMap.containsKey(bootstrapMethodIndex)) + { + LambdaExpression lambdaExpression = lambdaExpressionMap.get(bootstrapMethodIndex); + String lambdaClassName = lambdaExpression.getLambdaClassName(); + + InstructionSequenceBuilder builder = + new InstructionSequenceBuilder(programClass); + + if (lambdaExpression.isStateless()) + { + builder.getstatic(lambdaClassName, + LAMBDA_SINGLETON_FIELD_NAME, + ClassUtil.internalTypeFromClassName(lambdaClassName)); + } + else + { + int maxLocals = codeAttribute.u2maxLocals; + + String methodDescriptor = + lambdaExpression.getConstructorDescriptor(); + int parameterSize = + ClassUtil.internalMethodParameterSize(methodDescriptor); + + // TODO: the special optimization in case there is only 1 + // parameter has been disabled as the used stack + // manipulation instructions might confuse the class + // converter (testcase 1988). + if (parameterSize == 1 && false) + { + // If only 1 parameter is captured by the lambda expression, + // and it is a Category 1 value, we can avoid storing the + // current stack to variables. + builder.new_(lambdaClassName) + .dup_x1() + .swap() + .invokespecial(lambdaClassName, + ClassConstants.METHOD_NAME_INIT, + methodDescriptor); + } + else + { + // More than 1 (or a Category 2) parameter is captured + // by the lambda expression. We need to store the current + // call stack to variables, create the lambda instance and + // load the call stack again from the temporary variables. + + // Collect the argument types. + InternalTypeEnumeration typeEnumeration = + new InternalTypeEnumeration(methodDescriptor); + List types = new ArrayList(); + while(typeEnumeration.hasMoreTypes()) + { + types.add(typeEnumeration.nextType()); + } + + // Store the current call stack in reverse order + // into temporary variables. + int variableIndex = maxLocals; + ListIterator typeIterator = + types.listIterator(types.size()); + while (typeIterator.hasPrevious()) + { + String type = typeIterator.previous(); + + builder.store(variableIndex, type); + variableIndex += ClassUtil.internalTypeSize(type); + } + + // Create the lambda instance. + builder.new_(lambdaClassName); + builder.dup(); + + // Reconstruct the call stack by loading it from + // the temporary variables. + typeIterator = types.listIterator(); + while (typeIterator.hasNext()) + { + String type = typeIterator.next(); + + int variableSize = ClassUtil.internalTypeSize(type); + variableIndex -= variableSize; + builder.load(variableIndex, type); + } + + builder.invokespecial(lambdaClassName, + ClassConstants.METHOD_NAME_INIT, + methodDescriptor); + } + } + + codeAttributeEditor.replaceInstruction(offset, + builder.instructions()); + } + } + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) {} + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (isDeserializationHook(programClass, programMethod)) + { + memberRemover.visitProgramMethod(programClass, programMethod); + } + } + + + // Small utility methods. + + private static final String METHOD_NAME_DESERIALIZE_LAMBDA = "$deserializeLambda$"; + private static final String METHOD_TYPE_DESERIALIZE_LAMBDA = "(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;"; + + private static boolean isDeserializationHook(Clazz clazz, Method method) + { + return method.getName(clazz) .equals(METHOD_NAME_DESERIALIZE_LAMBDA) && + method.getDescriptor(clazz).equals(METHOD_TYPE_DESERIALIZE_LAMBDA) && + hasFlag(method, ClassConstants.ACC_PRIVATE | + ClassConstants.ACC_STATIC | + ClassConstants.ACC_SYNTHETIC); + } + + private static boolean hasFlag(Member member, int flag) + { + return (member.getAccessFlags() & flag) == flag; + } + + private ProgramClass createLambdaClass(LambdaExpression lambdaExpression) + { + String lambdaClassName = lambdaExpression.getLambdaClassName(); + + ProgramClass lambdaClass = + new ProgramClass(ClassConstants.CLASS_VERSION_1_5, + 1, + new Constant[10], + 0, + 0, + 0); + + ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor(lambdaClass); + + lambdaClass.u2thisClass = + constantPoolEditor.addClassConstant(lambdaClassName, lambdaClass); + lambdaClass.u2superClass = + constantPoolEditor.addClassConstant(ClassConstants.NAME_JAVA_LANG_OBJECT, + null); + + ClassEditor classEditor = new ClassEditor(lambdaClass); + + String[] interfaces = lambdaExpression.interfaces; + for (String interfaceName : interfaces) + { + classEditor.addInterface(constantPoolEditor.addClassConstant(interfaceName, + null)); + } + + // Store the created lambda class in the LambdaExpression + // data structure for later use. + lambdaExpression.lambdaClass = lambdaClass; + + // Apply some optimizations to the lambda expression to + // avoid creating accessor methods all the time. + //fixAccessFlags(lambdaExpression); + + // In case the invoked method can not be accessed directly + // by the lambda class, add a synthetic accessor method. + if (lambdaExpression.needsAccessorMethod()) + { + addAccessorMethod(lambdaExpression.referencedClass, + lambdaExpression); + } + + if (lambdaExpression.isStateless()) + { + completeStatelessLambdaClass(lambdaClass, lambdaExpression); + } + else + { + completeCapturingLambdaClass(lambdaClass, lambdaExpression); + } + + if (lambdaExpression.bridgeMethodDescriptors.length > 0) + { + addBridgeMethods(lambdaClass, lambdaExpression); + } + + return lambdaClass; + } + + + private void fixAccessFlags(LambdaExpression lambdaExpression) + { + // If the invoked method is private, we can make it package-private + // to be able to access it from the lambda class. Otherwise we would + // need to add an additional accessor method. + if (lambdaExpression.referencedInvokedMethod instanceof ProgramMethod) + { + ProgramMethod invokedProgramMethod = + (ProgramMethod) lambdaExpression.referencedInvokedMethod; + + int currentAccessFlags = invokedProgramMethod.getAccessFlags(); + if ((currentAccessFlags & ClassConstants.ACC_PRIVATE) != 0) + { + invokedProgramMethod.u2accessFlags = + AccessUtil.replaceAccessFlags(currentAccessFlags, + AccessUtil.accessFlags(AccessUtil.PACKAGE_VISIBLE)); + + // In case of instance-capturing lambdas or method references to private + // methods the reference kind is invokeSpecial. After fixing the + // access flags we need to update the reference kind as well. + if (lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeSpecial) + { + lambdaExpression.invokedReferenceKind = ClassConstants.REF_invokeVirtual; + } + } + } + } + + + private void addAccessorMethod(ProgramClass programClass, + LambdaExpression lambdaExpression) + { + SimplifiedClassEditor classEditor = new SimplifiedClassEditor(programClass); + + String className = programClass.getName(); + + // Create accessor method. + String shortClassName = + ClassUtil.externalShortClassName( + ClassUtil.externalClassName(className)); + + String accessorMethodName = + String.format("accessor$%s$lambda%d", + shortClassName, + lambdaExpression.bootstrapMethodIndex); + + String accessorMethodDescriptor = + lambdaExpression.invokedMethodDesc; + int accessFlags = + lambdaExpression.referencedInvokedMethod.getAccessFlags(); + + if ((accessFlags & ClassConstants.ACC_STATIC) == 0) + { + accessorMethodDescriptor = + prependParameterToMethodDescriptor(accessorMethodDescriptor, + ClassUtil.internalTypeFromClassType(className)); + } + + CompactCodeAttributeComposer composer = + classEditor.addMethod(ClassConstants.ACC_STATIC | + ClassConstants.ACC_SYNTHETIC, + accessorMethodName, + accessorMethodDescriptor, + 50); + + // Load the parameters first. + InternalTypeEnumeration typeEnumeration = + new InternalTypeEnumeration(accessorMethodDescriptor); + + completeInterfaceMethod(lambdaExpression, + composer, + 0, + typeEnumeration, + null); + + classEditor.finishEditing(); + + // Update the lambda expression to point to the created + // accessor method instead. + lambdaExpression.invokedClassName = programClass.getName(); + lambdaExpression.invokedMethodName = accessorMethodName; + lambdaExpression.invokedMethodDesc = accessorMethodDescriptor; + lambdaExpression.invokedReferenceKind = ClassConstants.REF_invokeStatic; + + lambdaExpression.referencedInvokedClass = programClass; + lambdaExpression.referencedInvokedMethod = programClass.findMethod(accessorMethodName, + accessorMethodDescriptor); + } + + + private void completeStatelessLambdaClass(ProgramClass lambdaClass, + LambdaExpression lambdaExpression) + { + String lambdaClassType = ClassUtil.internalTypeFromClassName(lambdaClass.getName()); + + SimplifiedClassEditor classEditor = new SimplifiedClassEditor(lambdaClass); + + // Add singleton field + classEditor.addField(ClassConstants.ACC_PUBLIC | + ClassConstants.ACC_STATIC | + ClassConstants.ACC_FINAL, + LAMBDA_SINGLETON_FIELD_NAME, + lambdaClassType); + + // Add the constructor. + CompactCodeAttributeComposer composer = + classEditor.addMethod(ClassConstants.ACC_PUBLIC, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT, + 10); + + composer.aload_0() + .invokespecial(ClassConstants.NAME_JAVA_LANG_OBJECT, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT) + .return_(); + + // Add static initializer. + composer = + classEditor.addMethod(ClassConstants.ACC_STATIC, + ClassConstants.METHOD_NAME_CLINIT, + ClassConstants.METHOD_TYPE_CLINIT, + 30); + + composer.new_(lambdaClass.getName()) + .dup() + .invokespecial(lambdaClass.getName(), + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT) + .putstatic(lambdaClass.getName(), + LAMBDA_SINGLETON_FIELD_NAME, + lambdaClassType) + .return_(); + + // If the lambda expression is serializable, create a readResolve method + // to return the singleton field. + if (lambdaExpression.isSerializable()) + { + composer = + classEditor.addMethod(ClassConstants.ACC_PRIVATE, + ClassConstants.METHOD_NAME_READ_RESOLVE, + ClassConstants.METHOD_TYPE_READ_RESOLVE, + 10); + + composer.getstatic(lambdaClass.getName(), + LAMBDA_SINGLETON_FIELD_NAME, + lambdaClassType) + .areturn(); + } + + // Add the interface method. + composer = + classEditor.addMethod(ClassConstants.ACC_PUBLIC, + lambdaExpression.interfaceMethod, + lambdaExpression.interfaceMethodDescriptor, + 50); + + if (lambdaExpression.invokedReferenceKind == ClassConstants.REF_newInvokeSpecial) + { + InternalTypeEnumeration typeEnumeration = + new InternalTypeEnumeration(lambdaExpression.interfaceMethodDescriptor); + + InternalTypeEnumeration invokedTypeEnumeration = + new InternalTypeEnumeration(lambdaExpression.invokedMethodDesc); + + composer.new_(lambdaExpression.invokedClassName) + .dup(); + + // Convert the remaining parameters if they are present. + completeInterfaceMethod(lambdaExpression, + composer, + 1, + typeEnumeration, + invokedTypeEnumeration); + } + else + { + InternalTypeEnumeration typeEnumeration = + new InternalTypeEnumeration(lambdaExpression.interfaceMethodDescriptor); + + InternalTypeEnumeration invokedTypeEnumeration = + new InternalTypeEnumeration(lambdaExpression.invokedMethodDesc); + + boolean isInvokeVirtualOrInterface = + lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeVirtual || + lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeInterface; + + int paramIndex = 1; + + // If we invoke a method on an object, we need to cast it to the invoked type. + if (isInvokeVirtualOrInterface) + { + String type = typeEnumeration.nextType(); + String invokedType = + ClassUtil.internalTypeFromClassName(lambdaExpression.invokedClassName); + + composer.load(paramIndex, type); + paramIndex += ClassUtil.internalTypeSize(type); + + convertToTargetType(type, invokedType, composer); + } + + // Convert the remaining parameters if they are present. + completeInterfaceMethod(lambdaExpression, + composer, + paramIndex, + typeEnumeration, + invokedTypeEnumeration); + } + + classEditor.finishEditing(); + } + + + private void completeCapturingLambdaClass(ProgramClass lambdaClass, + LambdaExpression lambdaExpression) + { + SimplifiedClassEditor classEditor = new SimplifiedClassEditor(lambdaClass); + + // Create constructor. + String ctorDescriptor = lambdaExpression.getConstructorDescriptor(); + CompactCodeAttributeComposer composer = + classEditor.addMethod(ClassConstants.ACC_PUBLIC, + ClassConstants.METHOD_NAME_INIT, + ctorDescriptor, + 50); + + composer.aload_0() + .invokespecial(ClassConstants.NAME_JAVA_LANG_OBJECT, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT); + + InternalTypeEnumeration typeEnumeration = + new InternalTypeEnumeration(ctorDescriptor); + + int argIndex = 0; + int variableIndex = 1; + while (typeEnumeration.hasMoreTypes()) + { + String type = typeEnumeration.nextType(); + String fieldName = "arg$" + argIndex++; + + classEditor.addField(ClassConstants.ACC_PRIVATE | ClassConstants.ACC_FINAL, + fieldName, + type); + + composer.aload_0(); + composer.load(variableIndex, type); + composer.putfield(lambdaClass.getName(), fieldName, type); + + variableIndex += ClassUtil.internalTypeSize(type); + } + + composer.return_(); + + // Create interface method. + composer = + classEditor.addMethod(ClassConstants.ACC_PUBLIC, + lambdaExpression.interfaceMethod, + lambdaExpression.interfaceMethodDescriptor, + 50); + + // Load the instance fields first. + typeEnumeration = + new InternalTypeEnumeration(ctorDescriptor); + + InternalTypeEnumeration invokedTypeEnumeration = + new InternalTypeEnumeration(lambdaExpression.invokedMethodDesc); + + boolean isInvokeVirtualOrInterface = + lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeVirtual || + lambdaExpression.invokedReferenceKind == ClassConstants.REF_invokeInterface; + + argIndex = 0; + while (typeEnumeration.hasMoreTypes()) + { + String type = typeEnumeration.nextType(); + String invokedType = isInvokeVirtualOrInterface && argIndex == 0 ? + null : invokedTypeEnumeration.nextType(); + + String fieldName = "arg$" + argIndex++; + + composer.aload_0() + .getfield(lambdaClass.getName(), fieldName, type); + + if (invokedType != null) + { + convertToTargetType(type, invokedType, composer); + } + } + + // And then the method parameters. + typeEnumeration = + new InternalTypeEnumeration(lambdaExpression.interfaceMethodDescriptor); + + completeInterfaceMethod(lambdaExpression, + composer, + 1, + typeEnumeration, + invokedTypeEnumeration); + + classEditor.finishEditing(); + } + + + private void completeInterfaceMethod(LambdaExpression lambdaExpression, + CompactCodeAttributeComposer composer, + int parameterIndex, + InternalTypeEnumeration typeEnumeration, + InternalTypeEnumeration invokedTypeEnumeration) + { + while (typeEnumeration.hasMoreTypes()) + { + String type = typeEnumeration.nextType(); + String invokedType = + invokedTypeEnumeration != null ? + invokedTypeEnumeration.nextType() : + null; + + composer.load(parameterIndex, type); + parameterIndex += ClassUtil.internalTypeSize(type); + + if (invokedType != null) + { + convertToTargetType(type, invokedType, composer); + } + } + + switch (lambdaExpression.invokedReferenceKind) + { + case ClassConstants.REF_invokeStatic: + if (lambdaExpression.invokesStaticInterfaceMethod()) + { + composer.invokestaticinterface(lambdaExpression.invokedClassName, + lambdaExpression.invokedMethodName, + lambdaExpression.invokedMethodDesc, + lambdaExpression.referencedInvokedClass, + lambdaExpression.referencedInvokedMethod); + } + else + { + composer.invokestatic(lambdaExpression.invokedClassName, + lambdaExpression.invokedMethodName, + lambdaExpression.invokedMethodDesc, + lambdaExpression.referencedInvokedClass, + lambdaExpression.referencedInvokedMethod); + } + break; + + case ClassConstants.REF_invokeVirtual: + composer.invokevirtual(lambdaExpression.invokedClassName, + lambdaExpression.invokedMethodName, + lambdaExpression.invokedMethodDesc, + lambdaExpression.referencedInvokedClass, + lambdaExpression.referencedInvokedMethod); + break; + + case ClassConstants.REF_invokeInterface: + composer.invokeinterface(lambdaExpression.invokedClassName, + lambdaExpression.invokedMethodName, + lambdaExpression.invokedMethodDesc, + lambdaExpression.referencedInvokedClass, + lambdaExpression.referencedInvokedMethod); + break; + + case ClassConstants.REF_newInvokeSpecial: + case ClassConstants.REF_invokeSpecial: + composer.invokespecial(lambdaExpression.invokedClassName, + lambdaExpression.invokedMethodName, + lambdaExpression.invokedMethodDesc, + lambdaExpression.referencedInvokedClass, + lambdaExpression.referencedInvokedMethod); + break; + } + + String methodReturnType = typeEnumeration.returnType(); + + if (invokedTypeEnumeration != null) + { + convertToTargetType(invokedTypeEnumeration.returnType(), + methodReturnType, + composer); + } + + composer.return_(methodReturnType); + } + + + private void addBridgeMethods(ProgramClass lambdaClass, LambdaExpression lambdaExpression) + { + SimplifiedClassEditor classEditor = new SimplifiedClassEditor(lambdaClass); + + String methodName = lambdaExpression.interfaceMethod; + for (String bridgeMethodDescriptor : lambdaExpression.bridgeMethodDescriptors) + { + Method method = lambdaClass.findMethod(methodName, bridgeMethodDescriptor); + if (method != null) + { + continue; + } + + CompactCodeAttributeComposer composer = + classEditor.addMethod(ClassConstants.ACC_PUBLIC | + ClassConstants.ACC_SYNTHETIC | + ClassConstants.ACC_BRIDGE, + methodName, + bridgeMethodDescriptor, + 50); + + composer.aload_0(); + + InternalTypeEnumeration interfaceTypeEnumeration = + new InternalTypeEnumeration(lambdaExpression.interfaceMethodDescriptor); + + InternalTypeEnumeration bridgeTypeEnumeration = + new InternalTypeEnumeration(bridgeMethodDescriptor); + int variableIndex = 1; + while (bridgeTypeEnumeration.hasMoreTypes()) + { + String type = bridgeTypeEnumeration.nextType(); + String interfaceType = interfaceTypeEnumeration.nextType(); + + composer.load(variableIndex, type); + variableIndex += ClassUtil.internalTypeSize(type); + + convertToTargetType(type, interfaceType, composer); + } + + composer.invokevirtual(lambdaClass.getName(), + lambdaExpression.interfaceMethod, + lambdaExpression.interfaceMethodDescriptor); + + String methodReturnType = bridgeTypeEnumeration.returnType(); + + convertToTargetType(interfaceTypeEnumeration.returnType(), + methodReturnType, + composer); + + composer.return_(methodReturnType); + } + + classEditor.finishEditing(); + } + + + private static String prependParameterToMethodDescriptor(String methodDescriptor, + String type) + { + StringBuilder methodDescBuilder = new StringBuilder(); + + methodDescBuilder.append('('); + methodDescBuilder.append(type); + + InternalTypeEnumeration typeEnumeration = + new InternalTypeEnumeration(methodDescriptor); + + while (typeEnumeration.hasMoreTypes()) + { + methodDescBuilder.append(typeEnumeration.nextType()); + } + + methodDescBuilder.append(')'); + methodDescBuilder.append(typeEnumeration.returnType()); + return methodDescBuilder.toString(); + } + + + /** + * Adds the required instructions to the provided CodeAttributeComposer + * to convert the current value on the stack to the given targetType. + */ + private static void convertToTargetType(String sourceType, + String targetType, + CompactCodeAttributeComposer composer) + { + if (ClassUtil.isInternalPrimitiveType(sourceType) && + !ClassUtil.isInternalPrimitiveType(targetType)) + { + // Perform auto-boxing. + switch (sourceType.charAt(0)) + { + case ClassConstants.TYPE_INT: + composer.invokestatic(ClassConstants.NAME_JAVA_LANG_INTEGER, + "valueOf", + "(I)Ljava/lang/Integer;"); + break; + + case ClassConstants.TYPE_BYTE: + composer.invokestatic(ClassConstants.NAME_JAVA_LANG_BYTE, + "valueOf", + "(B)Ljava/lang/Byte;"); + break; + + case ClassConstants.TYPE_CHAR: + composer.invokestatic(ClassConstants.NAME_JAVA_LANG_CHARACTER, + "valueOf", + "(C)Ljava/lang/Character;"); + break; + + case ClassConstants.TYPE_SHORT: + composer.invokestatic(ClassConstants.NAME_JAVA_LANG_SHORT, + "valueOf", + "(S)Ljava/lang/Short;"); + break; + + case ClassConstants.TYPE_BOOLEAN: + composer.invokestatic(ClassConstants.NAME_JAVA_LANG_BOOLEAN, + "valueOf", + "(Z)Ljava/lang/Boolean;"); + break; + + case ClassConstants.TYPE_LONG: + composer.invokestatic(ClassConstants.NAME_JAVA_LANG_LONG, + "valueOf", + "(J)Ljava/lang/Long;"); + break; + + case ClassConstants.TYPE_FLOAT: + composer.invokestatic(ClassConstants.NAME_JAVA_LANG_FLOAT, + "valueOf", + "(F)Ljava/lang/Float;"); + break; + + case ClassConstants.TYPE_DOUBLE: + composer.invokestatic(ClassConstants.NAME_JAVA_LANG_DOUBLE, + "valueOf", + "(D)Ljava/lang/Double;"); + break; + } + } + else if (!ClassUtil.isInternalPrimitiveType(sourceType) && + ClassUtil.isInternalPrimitiveType(targetType)) + { + boolean castRequired = sourceType.equals(ClassConstants.TYPE_JAVA_LANG_OBJECT); + + // Perform auto-unboxing. + switch (targetType.charAt(0)) + { + case ClassConstants.TYPE_INT: + if (castRequired) + { + composer.checkcast("java/lang/Number"); + } + composer.invokevirtual("java/lang/Number", + "intValue", + "()I"); + break; + + case ClassConstants.TYPE_BYTE: + if (castRequired) + { + composer.checkcast(ClassConstants.NAME_JAVA_LANG_BYTE); + } + composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_BYTE, + "byteValue", + "()B"); + break; + + case ClassConstants.TYPE_CHAR: + if (castRequired) + { + composer.checkcast(ClassConstants.NAME_JAVA_LANG_CHARACTER); + } + composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_CHARACTER, + "charValue", + "()C"); + break; + + case ClassConstants.TYPE_SHORT: + if (castRequired) + { + composer.checkcast(ClassConstants.NAME_JAVA_LANG_SHORT); + } + composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_SHORT, + "shortValue", + "()S"); + break; + + case ClassConstants.TYPE_BOOLEAN: + if (castRequired) + { + composer.checkcast(ClassConstants.NAME_JAVA_LANG_BOOLEAN); + } + composer.invokevirtual(ClassConstants.NAME_JAVA_LANG_BOOLEAN, + "booleanValue", + "()Z"); + break; + + case ClassConstants.TYPE_LONG: + if (castRequired) + { + composer.checkcast("java/lang/Number"); + } + composer.invokevirtual("java/lang/Number", + "longValue", + "()J"); + break; + + case ClassConstants.TYPE_FLOAT: + if (castRequired) + { + composer.checkcast("java/lang/Number"); + } + composer.invokevirtual("java/lang/Number", + "floatValue", + "()F"); + break; + + case ClassConstants.TYPE_DOUBLE: + if (castRequired) + { + composer.checkcast("java/lang/Number"); + } + composer.invokevirtual("java/lang/Number", + "doubleValue", + "()D"); + break; + } + } + else if (ClassUtil.isInternalClassType(sourceType) && + (ClassUtil.isInternalClassType(targetType) || + ClassUtil.isInternalArrayType(targetType)) && + !sourceType.equals(targetType) && + // No need to cast to java/lang/Object. + !ClassConstants.TYPE_JAVA_LANG_OBJECT.equals(targetType)) + { + // Cast to target type. + composer.checkcast(ClassUtil.internalClassTypeFromType(targetType)); + } + } +} diff --git a/core/src/proguard/backport/StaticInterfaceMethodConverter.java b/core/src/proguard/backport/StaticInterfaceMethodConverter.java new file mode 100644 index 000000000..7f3db7abc --- /dev/null +++ b/core/src/proguard/backport/StaticInterfaceMethodConverter.java @@ -0,0 +1,298 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.backport; + +import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AllAttributeVisitor; +import proguard.classfile.attribute.visitor.AttributeToClassVisitor; +import proguard.classfile.attribute.visitor.InstructionToAttributeVisitor; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.RefConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.ClassReferenceInitializer; +import proguard.classfile.util.ClassSuperHierarchyInitializer; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; +import proguard.optimize.peephole.InstructionSequencesReplacer; +import proguard.optimize.peephole.PeepholeOptimizer; +import proguard.util.MultiValueMap; + +import java.util.HashSet; +import java.util.Set; + +/** + * This ClassVisitor moves all static interface methods in the visited + * interfaces to a separate util class and updates all invocations in + * the program class pool. + * + * @author Thomas Neidhart + */ +public class StaticInterfaceMethodConverter +extends SimplifiedVisitor +implements ClassVisitor +{ + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final MultiValueMap injectedClassNameMap; + private final ClassVisitor modifiedClassVisitor; + private final MemberVisitor extraMemberVisitor; + + + public StaticInterfaceMethodConverter(ClassPool programClassPool, + ClassPool libraryClassPool, + MultiValueMap injectedClassNameMap, + ClassVisitor modifiedClassVisitor, + MemberVisitor extraMemberVisitor) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.injectedClassNameMap = injectedClassNameMap; + this.modifiedClassVisitor = modifiedClassVisitor; + this.extraMemberVisitor = extraMemberVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + // Collect all static methods of the interface class. + Set staticMethods = new HashSet(); + programClass.accept( + new AllMethodVisitor( + new MemberAccessFilter(ClassConstants.ACC_STATIC, 0, + new InitializerMethodFilter(null, + new MemberCollector(false, true, true, staticMethods))))); + + if (!staticMethods.isEmpty()) + { + ProgramClass utilityClass = createUtilityClass(programClass); + + // Copy all static interface methods to the utility class. + MemberVisitor memberAdder = new MemberAdder(utilityClass); + if (extraMemberVisitor != null) + { + memberAdder = + new MultiMemberVisitor( + memberAdder, + extraMemberVisitor + ); + } + + MemberRemover memberRemover = new MemberRemover(); + + programClass.accept( + new AllMethodVisitor( + new MemberAccessFilter(ClassConstants.ACC_STATIC, 0, + new InitializerMethodFilter(null, + new MultiMemberVisitor( + // Add the method to the utility class. + memberAdder, + + // Mark the method for removal from the + // interface class. + memberRemover + ) + )))); + + // Add the utility class to the program class pool + // and the injected class name map. + programClassPool.addClass(utilityClass); + injectedClassNameMap.put(programClass.getName(), utilityClass.getName()); + + // Change all invokestatic invocations of the static interface + // methods to use the utility class instead. + replaceInstructions(programClass, utilityClass, staticMethods); + + // Initialize the hierarchy and references of the utility class. + utilityClass.accept( + new MultiClassVisitor( + new ClassSuperHierarchyInitializer(programClassPool, libraryClassPool), + new ClassReferenceInitializer(programClassPool, libraryClassPool) + )); + + // Remove the static methods from the interface class and + // shrink the constant pool of unused constants. + programClass.accept( + new MultiClassVisitor( + memberRemover, + + new ConstantPoolShrinker() + )); + } + } + + + // Small utility methods. + + private ProgramClass createUtilityClass(ProgramClass interfaceClazz) + { + ProgramClass utilityClass = + new ProgramClass(ClassConstants.CLASS_VERSION_1_2, + 1, + new Constant[10], + 0, + 0, + 0); + + String utilityClassName = interfaceClazz.getName() + "$$Util"; + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(utilityClass, programClassPool, libraryClassPool); + + utilityClass.u2thisClass = + constantPoolEditor.addClassConstant(utilityClassName, + utilityClass); + utilityClass.u2superClass = + constantPoolEditor.addClassConstant(ClassConstants.NAME_JAVA_LANG_OBJECT, + null); + + SimplifiedClassEditor classEditor = + new SimplifiedClassEditor(utilityClass); + + // Add a private constructor. + classEditor.addMethod(ClassConstants.ACC_PRIVATE, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT, + 10) + .aload_0() + .invokespecial(ClassConstants.NAME_JAVA_LANG_OBJECT, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT) + .return_(); + + classEditor.finishEditing(); + return utilityClass; + } + + + /** + * Replaces all static invocations of the given methods in the given + * interface class by invocations of copies of these methods in the + * given utility class. + */ + private void replaceInstructions(ProgramClass interfaceClass, + ProgramClass utilityClass, + Set staticMethods) + { + InstructionSequenceBuilder ____ = + new InstructionSequenceBuilder(programClassPool, + libraryClassPool); + + Instruction[][][] instructions = + new Instruction[staticMethods.size()][][]; + + int index = 0; + for (String staticMethod : staticMethods) + { + String[] splitArray = staticMethod.split("\\."); + String methodName = splitArray[0]; + String methodDesc = splitArray[1]; + + Instruction[][] replacement = new Instruction[][] + { + ____.invokestatic_interface(interfaceClass.getName(), + methodName, + methodDesc).__(), + + ____.invokestatic(utilityClass.getName(), + methodName, + methodDesc).__(), + }; + + instructions[index++] = replacement; + } + + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + InstructionVisitor updatedClassVisitor = + new InstructionToAttributeVisitor( + new AttributeToClassVisitor( + modifiedClassVisitor)); + + programClassPool.classesAccept( + new MyReferencedClassFilter(interfaceClass, + new AllMethodVisitor( + new AllAttributeVisitor( + new PeepholeOptimizer(codeAttributeEditor, + new InstructionSequencesReplacer(____.constants(), + instructions, + null, + codeAttributeEditor, + updatedClassVisitor)))))); + } + + + /** + * This ClassVisitor delegates its visits to classes that + * reference a given class via any RefConstant. + */ + private static class MyReferencedClassFilter + extends SimplifiedVisitor + implements ClassVisitor, + ConstantVisitor + { + private final Clazz referencedClass; + private final ClassVisitor classVisitor; + + private boolean referenceClassFound; + + public MyReferencedClassFilter(Clazz referencedClass, + ClassVisitor classVisitor) + { + this.referencedClass = referencedClass; + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + referenceClassFound = false; + programClass.constantPoolEntriesAccept(this); + + if (referenceClassFound) + { + programClass.accept(classVisitor); + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + if (refConstant.referencedClass == referencedClass) + { + referenceClassFound = true; + } + } + } +} diff --git a/core/src/proguard/backport/StringConcatenationConverter.java b/core/src/proguard/backport/StringConcatenationConverter.java new file mode 100644 index 000000000..11668bb14 --- /dev/null +++ b/core/src/proguard/backport/StringConcatenationConverter.java @@ -0,0 +1,314 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.backport; + +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 java.util.*; + +/** + * This InstructionVistor converts all indy String Concatenations in the visited + * classes to StringBuilder-append chains. + * + * @author Tim Van Den Broecke + */ +public class StringConcatenationConverter +extends SimplifiedVisitor +implements InstructionVisitor, + + // Implementation interfaces. + AttributeVisitor, + BootstrapMethodInfoVisitor, + ConstantVisitor +{ + // Constants as per specification + private static final char C_VARIABLE_ARGUMENT = '\u0001'; + private static final char C_CONSTANT_ARGUMENT = '\u0002'; + + private final InstructionVisitor extraInstructionVisitor; + private final CodeAttributeEditor codeAttributeEditor; + + private InstructionSequenceBuilder appendChainComposer; + private int estimatedStringLength; + private int referencedBootstrapMethodIndex; + private String concatenationRecipe; + private int[] concatenationConstants; + + public StringConcatenationConverter(InstructionVisitor extraInstructionVisitor, + CodeAttributeEditor codeAttributeEditor) + { + this.extraInstructionVisitor = extraInstructionVisitor; + this.codeAttributeEditor = codeAttributeEditor; + } + + + // Implementations for InstructionVisitor. + + @Override + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + @Override + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + if (constantInstruction.opcode == InstructionConstants.OP_INVOKEDYNAMIC) + { + ProgramClass programClass = (ProgramClass) clazz; + + InvokeDynamicConstant invokeDynamicConstant = + (InvokeDynamicConstant) programClass.getConstant(constantInstruction.constantIndex); + + // Remember the referenced bootstrap method index and extract the recipe from it. + referencedBootstrapMethodIndex = invokeDynamicConstant.getBootstrapMethodAttributeIndex(); + concatenationRecipe = null; + concatenationConstants = null; + + programClass.attributesAccept(this); + + if (concatenationRecipe != null) + { + //if (isMakeConcatWithConstants(invokeDynamicConstant.getName(programClass))) + String descriptor = invokeDynamicConstant.getType(programClass); + + InstructionSequenceBuilder mainReplacementComposer = new InstructionSequenceBuilder(programClass); + appendChainComposer = new InstructionSequenceBuilder(programClass); + estimatedStringLength = 0; + + // Collect the argument types. + InternalTypeEnumeration typeEnumeration = new InternalTypeEnumeration(descriptor); + List types = new ArrayList(); + while (typeEnumeration.hasMoreTypes()) + { + types.add(typeEnumeration.nextType()); + } + + // Store the correct number of stack values in reverse + // order in local variables + int variableIndex = codeAttribute.u2maxLocals; + ListIterator typeIterator = types.listIterator(types.size()); + while (typeIterator.hasPrevious()) + { + String type = typeIterator.previous(); + + mainReplacementComposer.store(variableIndex, type); + variableIndex += ClassUtil.internalTypeSize(type); + } + + // Loop over the recipe. + // Push the local variables one by one, insert + // constants where necessary and create an append + // instruction chain. + typeIterator = types.listIterator(); + for (int argIndex = 0, constantCounter = 0; argIndex < concatenationRecipe.length(); argIndex++) + { + switch (concatenationRecipe.charAt(argIndex)) + { + case C_VARIABLE_ARGUMENT: + String type = typeIterator.next(); + estimatedStringLength += typicalStringLengthFromType(type); + + int variableSize = ClassUtil.internalTypeSize(type); + variableIndex -= variableSize; + + appendChainComposer.load(variableIndex, type) + .invokevirtual(ClassConstants.NAME_JAVA_LANG_STRING_BUILDER, + ClassConstants.METHOD_NAME_APPEND, + appendDescriptorFromInternalType(type)); + break; + + case C_CONSTANT_ARGUMENT: + int constantIndex = concatenationConstants[constantCounter++]; + + appendChainComposer.ldc_(constantIndex); + // Visit the constant to decide how it needs to be appended. + programClass.constantPoolEntryAccept(constantIndex, this); + break; + + default: + // Find where the String stops and append it + int nextArgIndex = nextArgIndex(concatenationRecipe, argIndex); + estimatedStringLength += nextArgIndex - argIndex; + appendChainComposer.ldc(concatenationRecipe.substring(argIndex, nextArgIndex)) + .invokevirtual(ClassConstants.NAME_JAVA_LANG_STRING_BUILDER, + ClassConstants.METHOD_NAME_APPEND, + ClassConstants.METHOD_TYPE_STRING_STRING_BUILDER); + + // Jump forward to the end of the String + argIndex = nextArgIndex - 1; + break; + } + } + + // Create a StringBuilder with the estimated initial size + mainReplacementComposer.new_( ClassConstants.NAME_JAVA_LANG_STRING_BUILDER) + .dup() + .pushInt( estimatedStringLength) + .invokespecial(ClassConstants.NAME_JAVA_LANG_STRING_BUILDER, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INT_VOID); + + // Attach the 'append' instruction chain + mainReplacementComposer.appendInstructions(appendChainComposer.instructions()); + + // Finish with StringBuilder.toString() + mainReplacementComposer.invokevirtual(ClassConstants.NAME_JAVA_LANG_STRING_BUILDER, + ClassConstants.METHOD_NAME_TOSTRING, + ClassConstants.METHOD_TYPE_TOSTRING); + + // Commit the code changes + codeAttributeEditor.replaceInstruction(offset, + mainReplacementComposer.instructions()); + + // Optionally let this instruction be visited some more + if (extraInstructionVisitor != null) + { + extraInstructionVisitor.visitConstantInstruction(clazz, + method, + codeAttribute, + offset, + constantInstruction); + } + } + } + } + + + // Implementations for AttributeVisitor. + + @Override + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + @Override + public void visitBootstrapMethodsAttribute(Clazz clazz, + BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + bootstrapMethodsAttribute.bootstrapMethodEntryAccept(clazz, referencedBootstrapMethodIndex, this); + } + + + // Implementations for BootstrapMethodInfoVisitor. + + @Override + public void visitBootstrapMethodInfo(Clazz clazz, + BootstrapMethodInfo bootstrapMethodInfo) + { + ProgramClass programClass = (ProgramClass) clazz; + + MethodHandleConstant bootstrapMethodHandle = + (MethodHandleConstant) programClass.getConstant(bootstrapMethodInfo.u2methodHandleIndex); + + if (isStringConcatFactory(bootstrapMethodHandle.getClassName(clazz))) + { + concatenationRecipe = + ((StringConstant) programClass.getConstant(bootstrapMethodInfo.u2methodArguments[0])).getString(programClass); + concatenationConstants = bootstrapMethodInfo.u2methodArgumentCount > 1 ? + Arrays.copyOfRange(bootstrapMethodInfo.u2methodArguments, 1, bootstrapMethodInfo.u2methodArgumentCount) : + new int[0]; + } + + } + + + // Implementations for ConstantVisitor. + + @Override + public void visitAnyConstant(Clazz clazz, Constant constant) + { + // append as Object by default. Override below if necessary. + estimatedStringLength += 16; + + appendChainComposer.invokevirtual(ClassConstants.NAME_JAVA_LANG_STRING_BUILDER, + ClassConstants.METHOD_NAME_APPEND, + ClassConstants.METHOD_TYPE_OBJECT_STRING_BUILDER); + } + + @Override + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + estimatedStringLength += stringConstant.getString(clazz).length(); + + appendChainComposer.invokevirtual(ClassConstants.NAME_JAVA_LANG_STRING_BUILDER, + ClassConstants.METHOD_NAME_APPEND, + ClassConstants.METHOD_TYPE_STRING_STRING_BUILDER); + } + + + // Small utility methods. + + private static boolean isStringConcatFactory(String className) + { + return ClassConstants.NAME_JAVA_LANG_INVOKE_STRING_CONCAT_FACTORY.equals(className); + } + + private static boolean isMakeConcat(String methodName) + { + return ClassConstants.METHOD_NAME_MAKE_CONCAT.equals(methodName); + } + + private static boolean isMakeConcatWithConstants(String methodName) + { + return ClassConstants.METHOD_NAME_MAKE_CONCAT_WITH_CONSTANTS.equals(methodName); + } + + private static int typicalStringLengthFromType(String internalTypeName) + { + return internalTypeName.equals(ClassConstants.TYPE_BOOLEAN) ? ClassConstants.MAXIMUM_BOOLEAN_AS_STRING_LENGTH : + internalTypeName.equals(ClassConstants.TYPE_CHAR) ? ClassConstants.MAXIMUM_CHAR_AS_STRING_LENGTH : + internalTypeName.equals(ClassConstants.TYPE_INT) ? ClassConstants.MAXIMUM_INT_AS_STRING_LENGTH : + internalTypeName.equals(ClassConstants.TYPE_LONG) ? ClassConstants.MAXIMUM_LONG_AS_STRING_LENGTH : + internalTypeName.equals(ClassConstants.TYPE_FLOAT) ? ClassConstants.MAXIMUM_FLOAT_AS_STRING_LENGTH : + internalTypeName.equals(ClassConstants.TYPE_DOUBLE) ? ClassConstants.MAXIMUM_DOUBLE_AS_STRING_LENGTH : + ClassConstants.DEFAULT_STRINGBUILDER_INIT_SIZE ; + } + + private static String appendDescriptorFromInternalType(String internalTypeName) + { + return internalTypeName.equals(ClassConstants.TYPE_BOOLEAN) ? ClassConstants.METHOD_TYPE_BOOLEAN_STRING_BUILDER : + internalTypeName.equals(ClassConstants.TYPE_CHAR) ? ClassConstants.METHOD_TYPE_CHAR_STRING_BUILDER : + internalTypeName.equals(ClassConstants.TYPE_INT) ? ClassConstants.METHOD_TYPE_INT_STRING_BUILDER : + internalTypeName.equals(ClassConstants.TYPE_LONG) ? ClassConstants.METHOD_TYPE_LONG_STRING_BUILDER : + internalTypeName.equals(ClassConstants.TYPE_FLOAT) ? ClassConstants.METHOD_TYPE_FLOAT_STRING_BUILDER : + internalTypeName.equals(ClassConstants.TYPE_DOUBLE) ? ClassConstants.METHOD_TYPE_DOUBLE_STRING_BUILDER : + internalTypeName.equals(ClassConstants.TYPE_JAVA_LANG_STRING) ? ClassConstants.METHOD_TYPE_STRING_STRING_BUILDER : + ClassConstants.METHOD_TYPE_OBJECT_STRING_BUILDER ; + } + + private static int nextArgIndex(String recipe, int fromIndex) + { + for(int i = fromIndex; i < recipe.length(); i++) + { + char c = recipe.charAt(i); + if (c == C_VARIABLE_ARGUMENT || + c == C_CONSTANT_ARGUMENT) + { + return i; + } + } + return recipe.length(); + } +} diff --git a/src/proguard/classfile/ClassConstants.java b/core/src/proguard/classfile/ClassConstants.java similarity index 58% rename from src/proguard/classfile/ClassConstants.java rename to core/src/proguard/classfile/ClassConstants.java index a5057ad66..41dbc68f4 100644 --- a/src/proguard/classfile/ClassConstants.java +++ b/core/src/proguard/classfile/ClassConstants.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,6 +27,8 @@ */ public class ClassConstants { + public static final byte[] JMOD_HEADER = new byte[] { 'J', 'M', 1, 0 }; + public static final String CLASS_FILE_EXTENSION = ".class"; public static final int MAGIC = 0xCAFEBABE; @@ -47,6 +49,8 @@ public class ClassConstants public static final int CLASS_VERSION_1_7_MINOR = 0; public static final int CLASS_VERSION_1_8_MAJOR = 52; public static final int CLASS_VERSION_1_8_MINOR = 0; + public static final int CLASS_VERSION_1_9_MAJOR = 53; + public static final int CLASS_VERSION_1_9_MINOR = 0; public static final int CLASS_VERSION_1_0 = (CLASS_VERSION_1_0_MAJOR << 16) | CLASS_VERSION_1_0_MINOR; public static final int CLASS_VERSION_1_2 = (CLASS_VERSION_1_2_MAJOR << 16) | CLASS_VERSION_1_2_MINOR; @@ -56,6 +60,7 @@ public class ClassConstants public static final int CLASS_VERSION_1_6 = (CLASS_VERSION_1_6_MAJOR << 16) | CLASS_VERSION_1_6_MINOR; public static final int CLASS_VERSION_1_7 = (CLASS_VERSION_1_7_MAJOR << 16) | CLASS_VERSION_1_7_MINOR; public static final int CLASS_VERSION_1_8 = (CLASS_VERSION_1_8_MAJOR << 16) | CLASS_VERSION_1_8_MINOR; + public static final int CLASS_VERSION_1_9 = (CLASS_VERSION_1_9_MAJOR << 16) | CLASS_VERSION_1_9_MINOR; public static final int ACC_PUBLIC = 0x0001; public static final int ACC_PRIVATE = 0x0002; @@ -73,9 +78,18 @@ public class ClassConstants public static final int ACC_ABSTRACT = 0x0400; public static final int ACC_STRICT = 0x0800; public static final int ACC_SYNTHETIC = 0x1000; - public static final int ACC_ANNOTATTION = 0x2000; + public static final int ACC_ANNOTATION = 0x2000; public static final int ACC_ENUM = 0x4000; public static final int ACC_MANDATED = 0x8000; + public static final int ACC_MODULE = 0x8000; + public static final int ACC_OPEN = 0x0020; + public static final int ACC_TRANSITIVE = 0x0020; + public static final int ACC_STATIC_PHASE = 0x0040; + + // Custom flags introduced by ProGuard + public static final int ACC_RENAMED = 0x00010000; // Marks whether a class or class member has been renamed. + public static final int ACC_REMOVED_METHODS = 0x00020000; // Marks whether a class has (at least one) methods removed. + public static final int ACC_REMOVED_FIELDS = 0x00040000; // Marks whether a class has (at least one) fields removed. public static final int VALID_ACC_CLASS = ACC_PUBLIC | ACC_FINAL | @@ -83,7 +97,8 @@ public class ClassConstants ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | - ACC_ANNOTATTION | + ACC_ANNOTATION | + ACC_MODULE | ACC_ENUM; public static final int VALID_ACC_FIELD = ACC_PUBLIC | ACC_PRIVATE | @@ -109,6 +124,17 @@ public class ClassConstants public static final int VALID_ACC_PARAMETER = ACC_FINAL | ACC_SYNTHETIC | ACC_MANDATED; + public static final int VALID_ACC_MODULE = ACC_OPEN | + ACC_SYNTHETIC | + ACC_MANDATED; + public static final int VALID_ACC_REQUIRES = ACC_TRANSITIVE | + ACC_STATIC_PHASE | + ACC_SYNTHETIC | + ACC_MANDATED; + public static final int VALID_ACC_EXPORTS = ACC_SYNTHETIC | + ACC_MANDATED; + public static final int VALID_ACC_OPENS = ACC_SYNTHETIC | + ACC_MANDATED; public static final int CONSTANT_Utf8 = 1; public static final int CONSTANT_Integer = 3; @@ -124,6 +150,10 @@ public class ClassConstants 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 CONSTANT_Module = 19; + public static final int CONSTANT_Package = 20; + + public static final int CONSTANT_PrimitiveArray = 21; public static final int REF_getField = 1; public static final int REF_getStatic = 2; @@ -135,6 +165,10 @@ public class ClassConstants public static final int REF_newInvokeSpecial = 8; public static final int REF_invokeInterface = 9; + public static final int FLAG_BRIDGES = 4; + public static final int FLAG_MARKERS = 2; + public static final int FLAG_SERIALIZABLE = 1; + public static final String ATTR_BootstrapMethods = "BootstrapMethods"; public static final String ATTR_SourceFile = "SourceFile"; public static final String ATTR_SourceDir = "SourceDir"; @@ -159,6 +193,13 @@ public class ClassConstants public static final String ATTR_RuntimeVisibleTypeAnnotations = "RuntimeVisibleTypeAnnotations"; public static final String ATTR_RuntimeInvisibleTypeAnnotations = "RuntimeInvisibleTypeAnnotations"; public static final String ATTR_AnnotationDefault = "AnnotationDefault"; + public static final String ATTR_Module = "Module"; + public static final String ATTR_ModuleMainClass = "ModuleMainClass"; + public static final String ATTR_ModulePackages = "ModulePackages"; + // TODO: More attributes. + public static final String ATTR_CharacterRangeTable = "CharacterRangeTable"; + public static final String ATTR_CompilationID = "CompilationID"; + public static final String ATTR_SourceID = "SourceID"; public static final int ANNOTATION_TARGET_ParameterGenericClass = 0x00; public static final int ANNOTATION_TARGET_ParameterGenericMethod = 0x01; @@ -183,6 +224,11 @@ public class ClassConstants public static final int ANNOTATION_TARGET_ArgumentGenericMethodReferenceNew = 0x4a; public static final int ANNOTATION_TARGET_ArgumentGenericMethodReference = 0x4b; + public static final int RESOLUTION_FLAG_DO_NOT_RESOLVE_BY_DEFAULT = 0x0001; + public static final int RESOLUTION_FLAG_WARN_DEPRECATED = 0x0002; + public static final int RESOLUTION_FLAG_WARN_DEPRECATED_FOR_REMOVAL = 0x0004; + public static final int RESOLUTION_FLAG_WARN_INCUBATING = 0x0008; + 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'; @@ -197,35 +243,48 @@ public class ClassConstants public static final char METHOD_ARGUMENTS_OPEN = '('; public static final char METHOD_ARGUMENTS_CLOSE = ')'; - public static final String PACKAGE_JAVA_LANG = "java/lang/"; - public static final String NAME_JAVA_LANG_OBJECT = "java/lang/Object"; - public static final String TYPE_JAVA_LANG_OBJECT = "Ljava/lang/Object;"; - public static final String NAME_JAVA_LANG_CLONEABLE = "java/lang/Cloneable"; - public static final String NAME_JAVA_LANG_THROWABLE = "java/lang/Throwable"; - public static final String NAME_JAVA_LANG_CLASS = "java/lang/Class"; - public static final String NAME_JAVA_LANG_STRING = "java/lang/String"; - public static final String NAME_JAVA_LANG_STRING_BUFFER = "java/lang/StringBuffer"; - public static final String NAME_JAVA_LANG_STRING_BUILDER = "java/lang/StringBuilder"; - public static final String NAME_JAVA_LANG_INVOKE_METHOD_HANDLE = "java/lang/invoke/MethodHandle"; - public static final String NAME_JAVA_LANG_INVOKE_METHOD_TYPE = "java/lang/invoke/MethodType"; - public static final String NAME_JAVA_LANG_VOID = "java/lang/Void"; - public static final String NAME_JAVA_LANG_BOOLEAN = "java/lang/Boolean"; - public static final String NAME_JAVA_LANG_BYTE = "java/lang/Byte"; - public static final String NAME_JAVA_LANG_SHORT = "java/lang/Short"; - public static final String NAME_JAVA_LANG_CHARACTER = "java/lang/Character"; - public static final String NAME_JAVA_LANG_INTEGER = "java/lang/Integer"; - public static final String NAME_JAVA_LANG_LONG = "java/lang/Long"; - public static final String NAME_JAVA_LANG_FLOAT = "java/lang/Float"; - public static final String NAME_JAVA_LANG_DOUBLE = "java/lang/Double"; - public static final String NAME_JAVA_LANG_MATH = "java/lang/Math"; - public static final String NAME_JAVA_LANG_SYSTEM = "java/lang/System"; - public static final String NAME_JAVA_LANG_RUNTIME = "java/lang/Runtime"; - public static final String NAME_JAVA_LANG_REFLECT_ARRAY = "java/lang/reflect/Array"; - public static final String NAME_JAVA_LANG_REFLECT_FIELD = "java/lang/reflect/Field"; - public static final String NAME_JAVA_LANG_REFLECT_METHOD = "java/lang/reflect/Method"; - public static final String NAME_JAVA_LANG_REFLECT_CONSTRUCTOR = "java/lang/reflect/Constructor"; - public static final String NAME_JAVA_LANG_REFLECT_ACCESSIBLE_OBJECT = "java/lang/reflect/AccessibleObject"; - public static final String NAME_JAVA_IO_SERIALIZABLE = "java/io/Serializable"; + public static final String PACKAGE_JAVA_LANG = "java/lang/"; + public static final String NAME_JAVA_LANG_OBJECT = "java/lang/Object"; + public static final String TYPE_JAVA_LANG_OBJECT = "Ljava/lang/Object;"; + public static final String NAME_JAVA_LANG_CLONEABLE = "java/lang/Cloneable"; + public static final String NAME_JAVA_LANG_THROWABLE = "java/lang/Throwable"; + public static final String NAME_JAVA_LANG_EXCEPTION = "java/lang/Exception"; + public static final String NAME_JAVA_LANG_CLASS = "java/lang/Class"; + public static final String TYPE_JAVA_LANG_CLASS = "Ljava/lang/Class;"; + public static final String NAME_JAVA_LANG_CLASS_LOADER = "java/lang/ClassLoader"; + public static final String NAME_JAVA_LANG_STRING = "java/lang/String"; + public static final String TYPE_JAVA_LANG_STRING = "Ljava/lang/String;"; + public static final String NAME_JAVA_LANG_STRING_BUFFER = "java/lang/StringBuffer"; + public static final String NAME_JAVA_LANG_STRING_BUILDER = "java/lang/StringBuilder"; + public static final String NAME_JAVA_LANG_INVOKE_METHOD_HANDLE = "java/lang/invoke/MethodHandle"; + public static final String NAME_JAVA_LANG_INVOKE_METHOD_TYPE = "java/lang/invoke/MethodType"; + public static final String NAME_JAVA_LANG_INVOKE_STRING_CONCAT_FACTORY = "java/lang/invoke/StringConcatFactory"; + public static final String NAME_JAVA_LANG_VOID = "java/lang/Void"; + public static final String NAME_JAVA_LANG_BOOLEAN = "java/lang/Boolean"; + public static final String NAME_JAVA_LANG_BYTE = "java/lang/Byte"; + public static final String NAME_JAVA_LANG_SHORT = "java/lang/Short"; + public static final String NAME_JAVA_LANG_CHARACTER = "java/lang/Character"; + public static final String NAME_JAVA_LANG_INTEGER = "java/lang/Integer"; + public static final String NAME_JAVA_LANG_LONG = "java/lang/Long"; + public static final String NAME_JAVA_LANG_FLOAT = "java/lang/Float"; + public static final String NAME_JAVA_LANG_DOUBLE = "java/lang/Double"; + public static final String NAME_JAVA_LANG_MATH = "java/lang/Math"; + public static final String NAME_JAVA_LANG_SYSTEM = "java/lang/System"; + public static final String NAME_JAVA_LANG_RUNTIME = "java/lang/Runtime"; + public static final String NAME_JAVA_LANG_REFLECT_ARRAY = "java/lang/reflect/Array"; + public static final String NAME_JAVA_LANG_REFLECT_FIELD = "java/lang/reflect/Field"; + public static final String NAME_JAVA_LANG_REFLECT_METHOD = "java/lang/reflect/Method"; + public static final String NAME_JAVA_LANG_REFLECT_CONSTRUCTOR = "java/lang/reflect/Constructor"; + public static final String NAME_JAVA_LANG_REFLECT_ACCESSIBLE_OBJECT = "java/lang/reflect/AccessibleObject"; + public static final String NAME_JAVA_IO_SERIALIZABLE = "java/io/Serializable"; + public static final String NAME_JAVA_IO_BYTE_ARRAY_INPUT_STREAM = "java/io/ByteArrayInputStream"; + public static final String NAME_JAVA_IO_DATA_INPUT_STREAM = "java/io/DataInputStream"; + public static final String NAME_JAVA_IO_INPUT_STREAM = "java/io/InputStream"; + public static final String NAME_JAVA_UTIL_MAP = "java/util/Map"; + public static final String TYPE_JAVA_UTIL_MAP = "Ljava/util/Map;"; + public static final String NAME_JAVA_UTIL_HASH_MAP = "java/util/HashMap"; + + public static final String NAME_ANDROID_UTIL_FLOAT_MATH = "android/util/FloatMath"; public static final String NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicIntegerFieldUpdater"; public static final String NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicLongFieldUpdater"; @@ -236,36 +295,98 @@ public class ClassConstants public static final String METHOD_NAME_CLINIT = ""; public static final String METHOD_TYPE_CLINIT = "()V"; - public static final String METHOD_NAME_CLASS_FOR_NAME = "forName"; - public static final String METHOD_TYPE_CLASS_FOR_NAME = "(Ljava/lang/String;)Ljava/lang/Class;"; - public static final String METHOD_NAME_CLASS_GET_COMPONENT_TYPE = "getComponentType"; - public static final String METHOD_TYPE_CLASS_GET_COMPONENT_TYPE = "()Ljava/lang/Class;"; - public static final String METHOD_NAME_CLASS_GET_FIELD = "getField"; - public static final String METHOD_TYPE_CLASS_GET_FIELD = "(Ljava/lang/String;)Ljava/lang/reflect/Field;"; - public static final String METHOD_NAME_CLASS_GET_DECLARED_FIELD = "getDeclaredField"; - public static final String METHOD_TYPE_CLASS_GET_DECLARED_FIELD = "(Ljava/lang/String;)Ljava/lang/reflect/Field;"; - public static final String CONSTRUCTOR_NAME_CLASS_GET_CONSTRUCTOR = "getConstructor"; - public static final String CONSTRUCTOR_TYPE_CLASS_GET_CONSTRUCTOR = "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"; - public static final String CONSTRUCTOR_NAME_CLASS_GET_DECLARED_CONSTRUCTOR = "getDeclaredConstructor"; - public static final String CONSTRUCTOR_TYPE_CLASS_GET_DECLARED_CONSTRUCTOR = "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"; - public static final String METHOD_NAME_CLASS_GET_METHOD = "getMethod"; - public static final String METHOD_TYPE_CLASS_GET_METHOD = "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"; - public static final String METHOD_NAME_CLASS_GET_DECLARED_METHOD = "getDeclaredMethod"; - public static final String METHOD_TYPE_CLASS_GET_DECLARED_METHOD = "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"; - public static final String METHOD_NAME_CLASS_GET_DECLARING_CLASS = "getDeclaringClass"; - public static final String METHOD_NAME_CLASS_GET_ENCLOSING_CLASS = "getEnclosingClass"; - public static final String METHOD_NAME_CLASS_GET_ENCLOSING_CONSTRUCTOR = "getEnclosingConstructor"; - public static final String METHOD_NAME_CLASS_GET_ENCLOSING_METHOD = "getEnclosingMethod"; - public static final String METHOD_NAME_GET_ANNOTATION = "getAnnotation"; - public static final String METHOD_NAME_GET_ANNOTATIONS = "getAnnotations"; - public static final String METHOD_NAME_GET_DECLARED_ANNOTATIONS = "getDeclaredAnnotations"; - public static final String METHOD_NAME_GET_PARAMETER_ANNOTATIONS = "getParameterAnnotations"; - public static final String METHOD_NAME_GET_TYPE_PREFIX = "getType"; - public static final String METHOD_NAME_GET_GENERIC_PREFIX = "getGeneric"; - public static final String METHOD_NAME_NEW_UPDATER = "newUpdater"; - public static final String METHOD_TYPE_NEW_INTEGER_UPDATER = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater;"; - public static final String METHOD_TYPE_NEW_LONG_UPDATER = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicLongFieldUpdater;"; - public static final String METHOD_TYPE_NEW_REFERENCE_UPDATER = "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;"; + public static final String METHOD_NAME_OBJECT_GET_CLASS = "getClass"; + public static final String METHOD_TYPE_OBJECT_GET_CLASS = "()Ljava/lang/Class;"; + public static final String METHOD_NAME_CLASS_FOR_NAME = "forName"; + public static final String METHOD_TYPE_CLASS_FOR_NAME = "(Ljava/lang/String;)Ljava/lang/Class;"; + public static final String METHOD_NAME_CLASS_IS_INSTANCE = "isInstance"; + public static final String METHOD_TYPE_CLASS_IS_INSTANCE = "(Ljava/lang/Object;)Z"; + public static final String METHOD_NAME_CLASS_GET_CLASS_LOADER = "getClassLoader"; + public static final String METHOD_NAME_CLASS_GET_COMPONENT_TYPE = "getComponentType"; + public static final String METHOD_TYPE_CLASS_GET_COMPONENT_TYPE = "()Ljava/lang/Class;"; + public static final String METHOD_NAME_CLASS_GET_FIELD = "getField"; + public static final String METHOD_TYPE_CLASS_GET_FIELD = "(Ljava/lang/String;)Ljava/lang/reflect/Field;"; + public static final String METHOD_NAME_CLASS_GET_DECLARED_FIELD = "getDeclaredField"; + public static final String METHOD_TYPE_CLASS_GET_DECLARED_FIELD = "(Ljava/lang/String;)Ljava/lang/reflect/Field;"; + public static final String METHOD_NAME_CLASS_GET_FIELDS = "getFields"; + public static final String METHOD_TYPE_CLASS_GET_FIELDS = "()[Ljava/lang/reflect/Field;"; + public static final String METHOD_NAME_CLASS_GET_DECLARED_FIELDS = "getDeclaredFields"; + public static final String METHOD_TYPE_CLASS_GET_DECLARED_FIELDS = "()[Ljava/lang/reflect/Field;"; + public static final String METHOD_NAME_CLASS_GET_CONSTRUCTOR = "getConstructor"; + public static final String METHOD_TYPE_CLASS_GET_CONSTRUCTOR = "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"; + public static final String METHOD_NAME_CLASS_GET_DECLARED_CONSTRUCTOR = "getDeclaredConstructor"; + public static final String METHOD_TYPE_CLASS_GET_DECLARED_CONSTRUCTOR = "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"; + public static final String METHOD_NAME_CLASS_GET_CONSTRUCTORS = "getConstructors"; + public static final String METHOD_TYPE_CLASS_GET_CONSTRUCTORS = "()[Ljava/lang/reflect/Constructor;"; + public static final String METHOD_NAME_CLASS_GET_DECLARED_CONSTRUCTORS = "getDeclaredConstructors"; + public static final String METHOD_TYPE_CLASS_GET_DECLARED_CONSTRUCTORS = "()[Ljava/lang/reflect/Constructor;"; + public static final String METHOD_NAME_CLASS_GET_METHOD = "getMethod"; + public static final String METHOD_TYPE_CLASS_GET_METHOD = "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"; + public static final String METHOD_NAME_CLASS_GET_DECLARED_METHOD = "getDeclaredMethod"; + public static final String METHOD_TYPE_CLASS_GET_DECLARED_METHOD = "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"; + public static final String METHOD_NAME_CLASS_GET_METHODS = "getMethods"; + public static final String METHOD_TYPE_CLASS_GET_METHODS = "()[Ljava/lang/reflect/Method;"; + public static final String METHOD_NAME_CLASS_GET_DECLARED_METHODS = "getDeclaredMethods"; + public static final String METHOD_TYPE_CLASS_GET_DECLARED_METHODS = "()[Ljava/lang/reflect/Method;"; + public static final String METHOD_NAME_FIND_CLASS = "findClass"; + public static final String METHOD_TYPE_FIND_CLASS = "(Ljava/lang/String;)Ljava/lang/Class;"; + public static final String METHOD_NAME_LOAD_CLASS = "loadClass"; + public static final String METHOD_TYPE_LOAD_CLASS = "(Ljava/lang/String;Z)Ljava/lang/Class;"; + public static final String METHOD_NAME_FIND_LIBRARY = "findLibrary"; + public static final String METHOD_TYPE_FIND_LIBRARY = "(Ljava/lang/String;)Ljava/lang/String;"; + public static final String METHOD_NAME_LOAD_LIBRARY = "loadLibrary"; + public static final String METHOD_TYPE_LOAD_LIBRARY = "(Ljava/lang/String;)V"; + public static final String METHOD_NAME_LOAD = "load"; + public static final String METHOD_NAME_DO_LOAD = "doLoad"; + public static final String METHOD_TYPE_LOAD = "(Ljava/lang/String;)V"; + public static final String METHOD_TYPE_LOAD2 = "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"; + public static final String METHOD_NAME_NATIVE_LOAD = "nativeLoad"; + public static final String METHOD_TYPE_NATIVE_LOAD = "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;"; + public static final String METHOD_NAME_MAP_LIBRARY_NAME = "mapLibraryName"; + public static final String METHOD_TYPE_MAP_LIBRARY_NAME = "(Ljava/lang/String;)Ljava/lang/String;"; + public static final String METHOD_NAME_GET_RUNTIME = "getRuntime"; + public static final String METHOD_TYPE_GET_RUNTIME = "()Ljava/lang/Runtime;"; + public static final String METHOD_NAME_CLASS_GET_DECLARING_CLASS = "getDeclaringClass"; + public static final String METHOD_NAME_CLASS_GET_ENCLOSING_CLASS = "getEnclosingClass"; + public static final String METHOD_NAME_CLASS_GET_ENCLOSING_CONSTRUCTOR = "getEnclosingConstructor"; + public static final String METHOD_NAME_CLASS_GET_ENCLOSING_METHOD = "getEnclosingMethod"; + public static final String METHOD_NAME_GET_ANNOTATION = "getAnnotation"; + public static final String METHOD_NAME_GET_ANNOTATIONS = "getAnnotations"; + public static final String METHOD_NAME_GET_DECLARED_ANNOTATIONS = "getDeclaredAnnotations"; + public static final String METHOD_NAME_GET_PARAMETER_ANNOTATIONS = "getParameterAnnotations"; + public static final String METHOD_NAME_GET_TYPE_PREFIX = "getType"; + public static final String METHOD_NAME_GET_GENERIC_PREFIX = "getGeneric"; + public static final String METHOD_NAME_NEW_UPDATER = "newUpdater"; + public static final String METHOD_TYPE_NEW_INTEGER_UPDATER = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater;"; + public static final String METHOD_TYPE_NEW_LONG_UPDATER = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicLongFieldUpdater;"; + public static final String METHOD_TYPE_NEW_REFERENCE_UPDATER = "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;"; + public static final String METHOD_NAME_FIELD_GET = "get"; + public static final String METHOD_TYPE_FIELD_GET = "(Ljava/lang/Object;)Ljava/lang/Object;"; + public static final String METHOD_NAME_FIELD_SET = "set"; + public static final String METHOD_TYPE_FIELD_SET = "(Ljava/lang/Object;Ljava/lang/Object;)V"; + public static final String METHOD_NAME_METHOD_INVOKE = "invoke"; + public static final String METHOD_TYPE_METHOD_INVOKE = "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"; + public static final String METHOD_NAME_CONSTRUCTOR_NEW_INSTANCE = "newInstance"; + public static final String METHOD_TYPE_CONSTRUCTOR_NEW_INSTANCE = "([Ljava/lang/Object;)Ljava/lang/Object;"; + public static final String METHOD_NAME_ARRAY_NEW_INSTANCE = "newInstance"; + public static final String METHOD_TYPE_ARRAY_NEW_INSTANCE = "(Ljava/lang/Class;I)Ljava/lang/Object;"; + public static final String METHOD_TYPE_ARRAY_NEW_INSTANCE2 = "(Ljava/lang/Class;[I)Ljava/lang/Object;"; + public static final String METHOD_NAME_ACCESSIBLE_OBJECT_SET_ACCESSIBLE = "setAccessible"; + public static final String METHOD_TYPE_ACCESSIBLE_OBJECT_SET_ACCESSIBLE = "(Z)V"; + public static final String METHOD_NAME_GET_CAUSE = "getCause"; + public static final String METHOD_TYPE_GET_CAUSE = "()Ljava/lang/Throwable;"; + public static final String METHOD_NAME_MAKE_CONCAT = "makeConcat"; + public static final String METHOD_NAME_MAKE_CONCAT_WITH_CONSTANTS = "makeConcatWithConstants"; + + // Serialization methods. + public static final String METHOD_NAME_READ_OBJECT = "readObject"; + public static final String METHOD_TYPE_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V"; + public static final String METHOD_NAME_READ_RESOLVE = "readResolve"; + public static final String METHOD_TYPE_READ_RESOLVE = "()Ljava/lang/Object;"; + public static final String METHOD_NAME_WRITE_OBJECT = "writeObject"; + public static final String METHOD_TYPE_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V"; + public static final String METHOD_NAME_WRITE_REPLACE = "writeReplace"; + public static final String METHOD_TYPE_WRITE_REPLACE = "()Ljava/lang/Object;"; public static final String METHOD_NAME_DOT_CLASS_JAVAC = "class$"; public static final String METHOD_TYPE_DOT_CLASS_JAVAC = "(Ljava/lang/String;)Ljava/lang/Class;"; @@ -277,9 +398,15 @@ public class ClassConstants public static final String METHOD_NAME_NEW_INSTANCE = "newInstance"; public static final String METHOD_TYPE_NEW_INSTANCE = "()Ljava/lang/Object;"; + public static final String METHOD_NAME_VALUE_OF = "valueOf"; + public static final String METHOD_TYPE_VALUE_OF = "(I)Ljava/lang/Integer;"; + + public static final String FIELD_NAME_TYPE = "TYPE"; + public static final String FIELD_TYPE_TYPE = "Ljava/lang/Class;"; public static final String METHOD_NAME_EQUALS = "equals"; public static final String METHOD_TYPE_EQUALS = "(Ljava/lang/Object;)Z"; public static final String METHOD_NAME_LENGTH = "length"; + public static final String METHOD_TYPE_LENGTH = "()I"; public static final String METHOD_NAME_VALUEOF = "valueOf"; public static final String METHOD_TYPE_VALUEOF_BOOLEAN = "(Z)Ljava/lang/String;"; public static final String METHOD_TYPE_VALUEOF_CHAR = "(C)Ljava/lang/String;"; @@ -288,10 +415,15 @@ public class ClassConstants public static final String METHOD_TYPE_VALUEOF_FLOAT = "(F)Ljava/lang/String;"; public static final String METHOD_TYPE_VALUEOF_DOUBLE = "(D)Ljava/lang/String;"; public static final String METHOD_TYPE_VALUEOF_OBJECT = "(Ljava/lang/Object;)Ljava/lang/String;"; + public static final String METHOD_NAME_INTERN = "intern"; + public static final String METHOD_TYPE_INTERN = "()Ljava/lang/String;"; - public static final String METHOD_TYPE_LENGTH = "()I"; public static final String METHOD_NAME_APPEND = "append"; + public static final String METHOD_TYPE_INT_VOID = "(I)V"; public static final String METHOD_TYPE_STRING_VOID = "(Ljava/lang/String;)V"; + public static final String METHOD_TYPE_BYTES_VOID = "([B)V"; + public static final String METHOD_TYPE_BYTES_INT_VOID = "([BI)V"; + public static final String METHOD_TYPE_CHARS_VOID = "([C)V"; public static final String METHOD_TYPE_BOOLEAN_STRING_BUFFER = "(Z)Ljava/lang/StringBuffer;"; public static final String METHOD_TYPE_CHAR_STRING_BUFFER = "(C)Ljava/lang/StringBuffer;"; public static final String METHOD_TYPE_INT_STRING_BUFFER = "(I)Ljava/lang/StringBuffer;"; @@ -334,12 +466,25 @@ public class ClassConstants public static final char TYPE_GENERIC_BOUND = ':'; public static final char TYPE_GENERIC_END = '>'; - 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_LINE_NUMBER_TABLE_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; + 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_PARAMETER_COUNT = 32; + public static final int TYPICAL_CODE_LENGTH = 8096; + public static final int TYPICAL_LINE_NUMBER_TABLE_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; + public static final int TYPICAL_BOOTSTRAP_METHODS_ATTRIBUTE_SIZE = 16; + + public static final int MAXIMUM_BOOLEAN_AS_STRING_LENGTH = 5; // false + public static final int MAXIMUM_CHAR_AS_STRING_LENGTH = 1; // any char + public static final int MAXIMUM_INT_AS_STRING_LENGTH = 11; //-2147483648 + public static final int MAXIMUM_LONG_AS_STRING_LENGTH = 20; //-9223372036854775808 + public static final int MAXIMUM_FLOAT_AS_STRING_LENGTH = 13; //-3.4028235E38 + public static final int MAXIMUM_DOUBLE_AS_STRING_LENGTH = 23; //-1.7976931348623157E308 + public static final int MAXIMUM_AT_HASHCODE_LENGTH = MAXIMUM_CHAR_AS_STRING_LENGTH + + MAXIMUM_INT_AS_STRING_LENGTH; + public static final int DEFAULT_STRINGBUILDER_INIT_SIZE = 16; + } diff --git a/src/proguard/classfile/ClassPool.java b/core/src/proguard/classfile/ClassPool.java similarity index 98% rename from src/proguard/classfile/ClassPool.java rename to core/src/proguard/classfile/ClassPool.java index 0904d1154..19cd72bc3 100644 --- a/src/proguard/classfile/ClassPool.java +++ b/core/src/proguard/classfile/ClassPool.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/Clazz.java b/core/src/proguard/classfile/Clazz.java similarity index 99% rename from src/proguard/classfile/Clazz.java rename to core/src/proguard/classfile/Clazz.java index dca81c4e6..ed4883b1b 100644 --- a/src/proguard/classfile/Clazz.java +++ b/core/src/proguard/classfile/Clazz.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/Field.java b/core/src/proguard/classfile/Field.java similarity index 94% rename from src/proguard/classfile/Field.java rename to core/src/proguard/classfile/Field.java index 15f82dc55..9ca10c469 100644 --- a/src/proguard/classfile/Field.java +++ b/core/src/proguard/classfile/Field.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/JavaConstants.java b/core/src/proguard/classfile/JavaConstants.java similarity index 91% rename from src/proguard/classfile/JavaConstants.java rename to core/src/proguard/classfile/JavaConstants.java index 9512b25b4..63eced0ca 100644 --- a/src/proguard/classfile/JavaConstants.java +++ b/core/src/proguard/classfile/JavaConstants.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -38,10 +38,12 @@ public interface JavaConstants public static final String CLASS_VERSION_1_6 = "1.6"; public static final String CLASS_VERSION_1_7 = "1.7"; public static final String CLASS_VERSION_1_8 = "1.8"; + public static final String CLASS_VERSION_1_9 = "1.9"; public static final String CLASS_VERSION_1_5_ALIAS = "5"; public static final String CLASS_VERSION_1_6_ALIAS = "6"; public static final String CLASS_VERSION_1_7_ALIAS = "7"; public static final String CLASS_VERSION_1_8_ALIAS = "8"; + public static final String CLASS_VERSION_1_9_ALIAS = "9"; public static final String ACC_PUBLIC = "public"; public static final String ACC_PRIVATE = "private"; @@ -63,6 +65,10 @@ public interface JavaConstants public static final String ACC_ENUM = "enum"; public static final String ACC_MANDATED = "mandated"; // public static final String ACC_CONSTRUCTOR = "constructor"; + public static final String ACC_MODULE = "module"; + public static final String ACC_OPEN = "open"; + public static final String ACC_TRANSITIVE = "transitive"; +// public static final String ACC_STATIC_PHASE = "static"; public static final char PACKAGE_SEPARATOR = '.'; public static final char INNER_CLASS_SEPARATOR = '.'; diff --git a/src/proguard/classfile/LibraryClass.java b/core/src/proguard/classfile/LibraryClass.java similarity index 99% rename from src/proguard/classfile/LibraryClass.java rename to core/src/proguard/classfile/LibraryClass.java index 26ddff9f7..fa81d6710 100644 --- a/src/proguard/classfile/LibraryClass.java +++ b/core/src/proguard/classfile/LibraryClass.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/LibraryField.java b/core/src/proguard/classfile/LibraryField.java similarity index 97% rename from src/proguard/classfile/LibraryField.java rename to core/src/proguard/classfile/LibraryField.java index 843a9f962..9826b8067 100644 --- a/src/proguard/classfile/LibraryField.java +++ b/core/src/proguard/classfile/LibraryField.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/LibraryMember.java b/core/src/proguard/classfile/LibraryMember.java similarity index 97% rename from src/proguard/classfile/LibraryMember.java rename to core/src/proguard/classfile/LibraryMember.java index 085e31a2d..84016d85d 100644 --- a/src/proguard/classfile/LibraryMember.java +++ b/core/src/proguard/classfile/LibraryMember.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/LibraryMethod.java b/core/src/proguard/classfile/LibraryMethod.java similarity index 97% rename from src/proguard/classfile/LibraryMethod.java rename to core/src/proguard/classfile/LibraryMethod.java index 81012edb9..3cb9d859e 100644 --- a/src/proguard/classfile/LibraryMethod.java +++ b/core/src/proguard/classfile/LibraryMethod.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/Member.java b/core/src/proguard/classfile/Member.java similarity index 96% rename from src/proguard/classfile/Member.java rename to core/src/proguard/classfile/Member.java index 92ccdd20c..e47d07827 100644 --- a/src/proguard/classfile/Member.java +++ b/core/src/proguard/classfile/Member.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/Method.java b/core/src/proguard/classfile/Method.java similarity index 94% rename from src/proguard/classfile/Method.java rename to core/src/proguard/classfile/Method.java index 2d48f8365..22faccbe3 100644 --- a/src/proguard/classfile/Method.java +++ b/core/src/proguard/classfile/Method.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/ProgramClass.java b/core/src/proguard/classfile/ProgramClass.java similarity index 84% rename from src/proguard/classfile/ProgramClass.java rename to core/src/proguard/classfile/ProgramClass.java index a2137f19f..d561d171d 100644 --- a/src/proguard/classfile/ProgramClass.java +++ b/core/src/proguard/classfile/ProgramClass.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -34,7 +34,13 @@ */ public class ProgramClass implements Clazz { - public int u4magic; + private static final int[] EMPTY_INTERFACES = new int[0]; + private static final ProgramField[] EMPTY_FIELDS = new ProgramField[0]; + private static final ProgramMethod[] EMPTY_METHODS = new ProgramMethod[0]; + private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; + + + // public int u4magic; public int u4version; public int u2constantPoolCount; public Constant[] constantPool; @@ -68,6 +74,72 @@ public class ProgramClass implements Clazz public ProgramClass() {} + /** + * Creates an initialized ProgramClass without fields, methods, attributes, + * or subclasses. + */ + public ProgramClass(int u4version, + int u2constantPoolCount, + Constant[] constantPool, + int u2accessFlags, + int u2thisClass, + int u2superClass) + { + this(u4version, + u2constantPoolCount, + constantPool, + u2accessFlags, + u2thisClass, + u2superClass, + 0, + EMPTY_INTERFACES, + 0, + EMPTY_FIELDS, + 0, + EMPTY_METHODS, + 0, + EMPTY_ATTRIBUTES, + null); + } + + + /** + * Creates an initialized ProgramClass. + */ + public ProgramClass(int u4version, + int u2constantPoolCount, + Constant[] constantPool, + int u2accessFlags, + int u2thisClass, + int u2superClass, + int u2interfacesCount, + int[] u2interfaces, + int u2fieldsCount, + ProgramField[] fields, + int u2methodsCount, + ProgramMethod[] methods, + int u2attributesCount, + Attribute[] attributes, + Clazz[] subClasses) + { + this.u4version = u4version; + this.u2constantPoolCount = u2constantPoolCount; + this.constantPool = constantPool; + this.u2accessFlags = u2accessFlags; + this.u2thisClass = u2thisClass; + this.u2superClass = u2superClass; + this.u2interfacesCount = u2interfacesCount; + this.u2interfaces = u2interfaces; + this.u2fieldsCount = u2fieldsCount; + this.fields = fields; + this.u2methodsCount = u2methodsCount; + this.methods = methods; + this.u2attributesCount = u2attributesCount; + this.attributes = attributes; + this.subClasses = subClasses; + } + + /** * Returns the Constant at the given index in the constant pool. */ @@ -216,9 +288,9 @@ public void addSubClass(Clazz clazz) 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; + Clazz[] newSubClasses = new Clazz[subClasses.length+1]; + System.arraycopy(subClasses, 0, newSubClasses, 0, subClasses.length); + subClasses = newSubClasses; } subClasses[subClasses.length-1] = clazz; diff --git a/src/proguard/classfile/ProgramField.java b/core/src/proguard/classfile/ProgramField.java similarity index 82% rename from src/proguard/classfile/ProgramField.java rename to core/src/proguard/classfile/ProgramField.java index 1d3140f2e..940314d11 100644 --- a/src/proguard/classfile/ProgramField.java +++ b/core/src/proguard/classfile/ProgramField.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -31,6 +31,9 @@ */ public class ProgramField extends ProgramMember implements Field { + private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; + + /** * An extra field pointing to the Clazz object referenced in the * descriptor string. This field is filled out by the {@link @@ -48,6 +51,23 @@ public ProgramField() } + /** + * Creates an initialized ProgramField without attributes. + */ + public ProgramField(int u2accessFlags, + int u2nameIndex, + int u2descriptorIndex, + Clazz referencedClass) + { + this(u2accessFlags, + u2nameIndex, + u2descriptorIndex, + 0, + EMPTY_ATTRIBUTES, + referencedClass); + } + + /** * Creates an initialized ProgramField. */ diff --git a/src/proguard/classfile/ProgramMember.java b/core/src/proguard/classfile/ProgramMember.java similarity index 98% rename from src/proguard/classfile/ProgramMember.java rename to core/src/proguard/classfile/ProgramMember.java index 8736bf83c..1a0d9f877 100644 --- a/src/proguard/classfile/ProgramMember.java +++ b/core/src/proguard/classfile/ProgramMember.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/ProgramMethod.java b/core/src/proguard/classfile/ProgramMethod.java similarity index 84% rename from src/proguard/classfile/ProgramMethod.java rename to core/src/proguard/classfile/ProgramMethod.java index b13f2ef9a..8a9a529d3 100644 --- a/src/proguard/classfile/ProgramMethod.java +++ b/core/src/proguard/classfile/ProgramMethod.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -31,6 +31,9 @@ */ public class ProgramMethod extends ProgramMember implements Method { + private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; + + /** * An extra field containing all the classes referenced in the * descriptor string. This field is filled out by the {@link @@ -50,6 +53,23 @@ public ProgramMethod() } + /** + * Creates an initialized ProgramMethod without attributes. + */ + public ProgramMethod(int u2accessFlags, + int u2nameIndex, + int u2descriptorIndex, + Clazz[] referencedClasses) + { + this(u2accessFlags, + u2nameIndex, + u2descriptorIndex, + 0, + EMPTY_ATTRIBUTES, + referencedClasses); + } + + /** * Creates an initialized ProgramMethod. */ diff --git a/src/proguard/classfile/VisitorAccepter.java b/core/src/proguard/classfile/VisitorAccepter.java similarity index 96% rename from src/proguard/classfile/VisitorAccepter.java rename to core/src/proguard/classfile/VisitorAccepter.java index d06766adf..f4f1efbc4 100644 --- a/src/proguard/classfile/VisitorAccepter.java +++ b/core/src/proguard/classfile/VisitorAccepter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/Attribute.java b/core/src/proguard/classfile/attribute/Attribute.java similarity index 98% rename from src/proguard/classfile/attribute/Attribute.java rename to core/src/proguard/classfile/attribute/Attribute.java index 4a475a02a..fe0ee5d78 100644 --- a/src/proguard/classfile/attribute/Attribute.java +++ b/core/src/proguard/classfile/attribute/Attribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/BootstrapMethodInfo.java b/core/src/proguard/classfile/attribute/BootstrapMethodInfo.java old mode 100755 new mode 100644 similarity index 97% rename from src/proguard/classfile/attribute/BootstrapMethodInfo.java rename to core/src/proguard/classfile/attribute/BootstrapMethodInfo.java index 62902571a..0e1803dc4 --- a/src/proguard/classfile/attribute/BootstrapMethodInfo.java +++ b/core/src/proguard/classfile/attribute/BootstrapMethodInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/BootstrapMethodsAttribute.java b/core/src/proguard/classfile/attribute/BootstrapMethodsAttribute.java old mode 100755 new mode 100644 similarity index 98% rename from src/proguard/classfile/attribute/BootstrapMethodsAttribute.java rename to core/src/proguard/classfile/attribute/BootstrapMethodsAttribute.java index 08970e1d0..4e62455c9 --- a/src/proguard/classfile/attribute/BootstrapMethodsAttribute.java +++ b/core/src/proguard/classfile/attribute/BootstrapMethodsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/CodeAttribute.java b/core/src/proguard/classfile/attribute/CodeAttribute.java similarity index 84% rename from src/proguard/classfile/attribute/CodeAttribute.java rename to core/src/proguard/classfile/attribute/CodeAttribute.java index 0afd5dcbf..970d0b734 100644 --- a/src/proguard/classfile/attribute/CodeAttribute.java +++ b/core/src/proguard/classfile/attribute/CodeAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -32,6 +32,11 @@ */ public class CodeAttribute extends Attribute { + private static final byte[] EMPTY_CODE = new byte[0]; + private static final ExceptionInfo[] EMPTY_EXCEPTION_TABLE = new ExceptionInfo[0]; + private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; + + public int u2maxStack; public int u2maxLocals; public int u4codeLength; @@ -50,6 +55,41 @@ public CodeAttribute() } + /** + * Creates a partially initialized CodeAttribute without code, exceptions, + * or attributes. + */ + public CodeAttribute(int u2attributeNameIndex) + { + this(u2attributeNameIndex, + 0, + 0, + 0, + EMPTY_CODE); + } + + + /** + * Creates an initialized CodeAttribute without exceptions or attributes. + */ + public CodeAttribute(int u2attributeNameIndex, + int u2maxStack, + int u2maxLocals, + int u4codeLength, + byte[] code) + { + this(u2attributeNameIndex, + u2maxStack, + u2maxLocals, + u4codeLength, + code, + 0, + EMPTY_EXCEPTION_TABLE, + 0, + EMPTY_ATTRIBUTES); + } + + /** * Creates an initialized CodeAttribute. */ diff --git a/src/proguard/classfile/attribute/ConstantValueAttribute.java b/core/src/proguard/classfile/attribute/ConstantValueAttribute.java similarity index 96% rename from src/proguard/classfile/attribute/ConstantValueAttribute.java rename to core/src/proguard/classfile/attribute/ConstantValueAttribute.java index c4d9d808b..66fa84aad 100644 --- a/src/proguard/classfile/attribute/ConstantValueAttribute.java +++ b/core/src/proguard/classfile/attribute/ConstantValueAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/DeprecatedAttribute.java b/core/src/proguard/classfile/attribute/DeprecatedAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/DeprecatedAttribute.java rename to core/src/proguard/classfile/attribute/DeprecatedAttribute.java index ad3ac4be9..326a7e59d 100644 --- a/src/proguard/classfile/attribute/DeprecatedAttribute.java +++ b/core/src/proguard/classfile/attribute/DeprecatedAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/EnclosingMethodAttribute.java b/core/src/proguard/classfile/attribute/EnclosingMethodAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/EnclosingMethodAttribute.java rename to core/src/proguard/classfile/attribute/EnclosingMethodAttribute.java index a01fc5a7a..75c0e76d6 100644 --- a/src/proguard/classfile/attribute/EnclosingMethodAttribute.java +++ b/core/src/proguard/classfile/attribute/EnclosingMethodAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/ExceptionInfo.java b/core/src/proguard/classfile/attribute/ExceptionInfo.java similarity index 94% rename from src/proguard/classfile/attribute/ExceptionInfo.java rename to core/src/proguard/classfile/attribute/ExceptionInfo.java index 5c35e6f12..d0550b786 100644 --- a/src/proguard/classfile/attribute/ExceptionInfo.java +++ b/core/src/proguard/classfile/attribute/ExceptionInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -45,12 +45,11 @@ public class ExceptionInfo implements VisitorAccepter */ public ExceptionInfo() { - this(0, 0, 0, 0); } /** - * Creates an ExceptionInfo with the given properties. + * Creates an initialized ExceptionInfo. */ public ExceptionInfo(int u2startPC, int u2endPC, diff --git a/src/proguard/classfile/attribute/ExceptionsAttribute.java b/core/src/proguard/classfile/attribute/ExceptionsAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/ExceptionsAttribute.java rename to core/src/proguard/classfile/attribute/ExceptionsAttribute.java index a47fc57e3..e2487ee5a 100644 --- a/src/proguard/classfile/attribute/ExceptionsAttribute.java +++ b/core/src/proguard/classfile/attribute/ExceptionsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/ExtendedLineNumberInfo.java b/core/src/proguard/classfile/attribute/ExtendedLineNumberInfo.java similarity index 96% rename from src/proguard/classfile/attribute/ExtendedLineNumberInfo.java rename to core/src/proguard/classfile/attribute/ExtendedLineNumberInfo.java index 8f910bdb8..ff902ecf4 100644 --- a/src/proguard/classfile/attribute/ExtendedLineNumberInfo.java +++ b/core/src/proguard/classfile/attribute/ExtendedLineNumberInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/InnerClassesAttribute.java b/core/src/proguard/classfile/attribute/InnerClassesAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/InnerClassesAttribute.java rename to core/src/proguard/classfile/attribute/InnerClassesAttribute.java index bb81ece46..eb493de0b 100644 --- a/src/proguard/classfile/attribute/InnerClassesAttribute.java +++ b/core/src/proguard/classfile/attribute/InnerClassesAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/InnerClassesInfo.java b/core/src/proguard/classfile/attribute/InnerClassesInfo.java similarity index 85% rename from src/proguard/classfile/attribute/InnerClassesInfo.java rename to core/src/proguard/classfile/attribute/InnerClassesInfo.java index 21938ea29..10a8f32ef 100644 --- a/src/proguard/classfile/attribute/InnerClassesInfo.java +++ b/core/src/proguard/classfile/attribute/InnerClassesInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -41,31 +41,6 @@ public class InnerClassesInfo implements VisitorAccepter 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. diff --git a/src/proguard/classfile/attribute/LineNumberInfo.java b/core/src/proguard/classfile/attribute/LineNumberInfo.java similarity index 96% rename from src/proguard/classfile/attribute/LineNumberInfo.java rename to core/src/proguard/classfile/attribute/LineNumberInfo.java index ab0ee32f0..0d6786bd1 100644 --- a/src/proguard/classfile/attribute/LineNumberInfo.java +++ b/core/src/proguard/classfile/attribute/LineNumberInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/LineNumberTableAttribute.java b/core/src/proguard/classfile/attribute/LineNumberTableAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/LineNumberTableAttribute.java rename to core/src/proguard/classfile/attribute/LineNumberTableAttribute.java index bc9dd4d60..99b538ffb 100644 --- a/src/proguard/classfile/attribute/LineNumberTableAttribute.java +++ b/core/src/proguard/classfile/attribute/LineNumberTableAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/LocalVariableInfo.java b/core/src/proguard/classfile/attribute/LocalVariableInfo.java similarity index 98% rename from src/proguard/classfile/attribute/LocalVariableInfo.java rename to core/src/proguard/classfile/attribute/LocalVariableInfo.java index 3cdc66ed9..274b3feaa 100644 --- a/src/proguard/classfile/attribute/LocalVariableInfo.java +++ b/core/src/proguard/classfile/attribute/LocalVariableInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/LocalVariableTableAttribute.java b/core/src/proguard/classfile/attribute/LocalVariableTableAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/LocalVariableTableAttribute.java rename to core/src/proguard/classfile/attribute/LocalVariableTableAttribute.java index 67fe03120..0338208c5 100644 --- a/src/proguard/classfile/attribute/LocalVariableTableAttribute.java +++ b/core/src/proguard/classfile/attribute/LocalVariableTableAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/LocalVariableTypeInfo.java b/core/src/proguard/classfile/attribute/LocalVariableTypeInfo.java similarity index 98% rename from src/proguard/classfile/attribute/LocalVariableTypeInfo.java rename to core/src/proguard/classfile/attribute/LocalVariableTypeInfo.java index 918d0991b..e06049420 100644 --- a/src/proguard/classfile/attribute/LocalVariableTypeInfo.java +++ b/core/src/proguard/classfile/attribute/LocalVariableTypeInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java b/core/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java rename to core/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java index 1c644347a..db9f488ad 100644 --- a/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java +++ b/core/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/MethodParametersAttribute.java b/core/src/proguard/classfile/attribute/MethodParametersAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/MethodParametersAttribute.java rename to core/src/proguard/classfile/attribute/MethodParametersAttribute.java index 30086ae74..079d3bd86 100644 --- a/src/proguard/classfile/attribute/MethodParametersAttribute.java +++ b/core/src/proguard/classfile/attribute/MethodParametersAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/ParameterInfo.java b/core/src/proguard/classfile/attribute/ParameterInfo.java similarity index 81% rename from src/proguard/classfile/attribute/ParameterInfo.java rename to core/src/proguard/classfile/attribute/ParameterInfo.java index e0a8f32b5..4f1200d5a 100644 --- a/src/proguard/classfile/attribute/ParameterInfo.java +++ b/core/src/proguard/classfile/attribute/ParameterInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,6 +21,7 @@ package proguard.classfile.attribute; import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; /** * Representation of a parameter, as defined in a method parameters @@ -67,6 +68,19 @@ public String getName(Clazz clazz) } + /** + * Applies the given constant pool visitor to the Utf8 constant that + * represents the name of the parameter, if any. + */ + public void nameConstantAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2nameIndex != 0) + { + clazz.constantPoolEntryAccept(u2nameIndex, constantVisitor); + } + } + + // Implementations for VisitorAccepter. public Object getVisitorInfo() diff --git a/src/proguard/classfile/attribute/SignatureAttribute.java b/core/src/proguard/classfile/attribute/SignatureAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/SignatureAttribute.java rename to core/src/proguard/classfile/attribute/SignatureAttribute.java index 943056ff9..20ec26bb5 100644 --- a/src/proguard/classfile/attribute/SignatureAttribute.java +++ b/core/src/proguard/classfile/attribute/SignatureAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/SourceDirAttribute.java b/core/src/proguard/classfile/attribute/SourceDirAttribute.java similarity index 96% rename from src/proguard/classfile/attribute/SourceDirAttribute.java rename to core/src/proguard/classfile/attribute/SourceDirAttribute.java index 10328cf92..cc46f593c 100644 --- a/src/proguard/classfile/attribute/SourceDirAttribute.java +++ b/core/src/proguard/classfile/attribute/SourceDirAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/SourceFileAttribute.java b/core/src/proguard/classfile/attribute/SourceFileAttribute.java similarity index 96% rename from src/proguard/classfile/attribute/SourceFileAttribute.java rename to core/src/proguard/classfile/attribute/SourceFileAttribute.java index 86535b4b3..a3c5cd0a4 100644 --- a/src/proguard/classfile/attribute/SourceFileAttribute.java +++ b/core/src/proguard/classfile/attribute/SourceFileAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/SyntheticAttribute.java b/core/src/proguard/classfile/attribute/SyntheticAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/SyntheticAttribute.java rename to core/src/proguard/classfile/attribute/SyntheticAttribute.java index b3120db19..fbfb80cc4 100644 --- a/src/proguard/classfile/attribute/SyntheticAttribute.java +++ b/core/src/proguard/classfile/attribute/SyntheticAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/UnknownAttribute.java b/core/src/proguard/classfile/attribute/UnknownAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/UnknownAttribute.java rename to core/src/proguard/classfile/attribute/UnknownAttribute.java index 6654bf6f4..41dfccc49 100644 --- a/src/proguard/classfile/attribute/UnknownAttribute.java +++ b/core/src/proguard/classfile/attribute/UnknownAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/Annotation.java b/core/src/proguard/classfile/attribute/annotation/Annotation.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/Annotation.java rename to core/src/proguard/classfile/attribute/annotation/Annotation.java index 1b02799e8..19ed8b198 100644 --- a/src/proguard/classfile/attribute/annotation/Annotation.java +++ b/core/src/proguard/classfile/attribute/annotation/Annotation.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java b/core/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java rename to core/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java index 85fc79250..8895419d9 100644 --- a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java b/core/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/AnnotationElementValue.java rename to core/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java index b957f82f5..25d521709 100644 --- a/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java +++ b/core/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java index 167710e01..6aba0aeea 100644 --- a/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/ArrayElementValue.java b/core/src/proguard/classfile/attribute/annotation/ArrayElementValue.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/ArrayElementValue.java rename to core/src/proguard/classfile/attribute/annotation/ArrayElementValue.java index 06c84177e..589f12aca 100644 --- a/src/proguard/classfile/attribute/annotation/ArrayElementValue.java +++ b/core/src/proguard/classfile/attribute/annotation/ArrayElementValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/ClassElementValue.java b/core/src/proguard/classfile/attribute/annotation/ClassElementValue.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/ClassElementValue.java rename to core/src/proguard/classfile/attribute/annotation/ClassElementValue.java index 945d280a2..1a60974f8 100644 --- a/src/proguard/classfile/attribute/annotation/ClassElementValue.java +++ b/core/src/proguard/classfile/attribute/annotation/ClassElementValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java b/core/src/proguard/classfile/attribute/annotation/ConstantElementValue.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/ConstantElementValue.java rename to core/src/proguard/classfile/attribute/annotation/ConstantElementValue.java index 0e7a4b6ff..86b3c47e7 100644 --- a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java +++ b/core/src/proguard/classfile/attribute/annotation/ConstantElementValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/ElementValue.java b/core/src/proguard/classfile/attribute/annotation/ElementValue.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/ElementValue.java rename to core/src/proguard/classfile/attribute/annotation/ElementValue.java index 8d18c1f5e..c252c0ffb 100644 --- a/src/proguard/classfile/attribute/annotation/ElementValue.java +++ b/core/src/proguard/classfile/attribute/annotation/ElementValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java b/core/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java rename to core/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java index 8a82cd79a..fe1d5dc94 100644 --- a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java +++ b/core/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java index 666762f4a..177c7f2af 100644 --- a/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java index 6e2a416d8..5a0160f9c 100644 --- a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java index b9772bd22..ce920b67e 100644 --- a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleTypeAnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleTypeAnnotationsAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/RuntimeInvisibleTypeAnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleTypeAnnotationsAttribute.java index 6addf96dc..d265ceda7 100644 --- a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleTypeAnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/RuntimeInvisibleTypeAnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java index 7b5ff0030..ceeca3984 100644 --- a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java index a1a2e1aba..b2d3330a9 100644 --- a/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleTypeAnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/RuntimeVisibleTypeAnnotationsAttribute.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/RuntimeVisibleTypeAnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/RuntimeVisibleTypeAnnotationsAttribute.java index 2652a1f6d..66c049e85 100644 --- a/src/proguard/classfile/attribute/annotation/RuntimeVisibleTypeAnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/RuntimeVisibleTypeAnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/TypeAnnotation.java b/core/src/proguard/classfile/attribute/annotation/TypeAnnotation.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/TypeAnnotation.java rename to core/src/proguard/classfile/attribute/annotation/TypeAnnotation.java index 1246dfa63..45861905a 100644 --- a/src/proguard/classfile/attribute/annotation/TypeAnnotation.java +++ b/core/src/proguard/classfile/attribute/annotation/TypeAnnotation.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/TypeAnnotationsAttribute.java b/core/src/proguard/classfile/attribute/annotation/TypeAnnotationsAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/TypeAnnotationsAttribute.java rename to core/src/proguard/classfile/attribute/annotation/TypeAnnotationsAttribute.java index 644d04548..6423ab94b 100644 --- a/src/proguard/classfile/attribute/annotation/TypeAnnotationsAttribute.java +++ b/core/src/proguard/classfile/attribute/annotation/TypeAnnotationsAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/TypePathInfo.java b/core/src/proguard/classfile/attribute/annotation/TypePathInfo.java similarity index 96% rename from src/proguard/classfile/attribute/annotation/TypePathInfo.java rename to core/src/proguard/classfile/attribute/annotation/TypePathInfo.java index 1aa91f266..f5669623c 100644 --- a/src/proguard/classfile/attribute/annotation/TypePathInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/TypePathInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/package.html b/core/src/proguard/classfile/attribute/annotation/package.html similarity index 100% rename from src/proguard/classfile/attribute/annotation/package.html rename to core/src/proguard/classfile/attribute/annotation/package.html diff --git a/src/proguard/classfile/attribute/annotation/target/CatchTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/CatchTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/CatchTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/CatchTargetInfo.java index 81c709d6b..bf909cdc4 100644 --- a/src/proguard/classfile/attribute/annotation/target/CatchTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/CatchTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/EmptyTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/EmptyTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/EmptyTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/EmptyTargetInfo.java index 6b6a9c9b9..940b97b03 100644 --- a/src/proguard/classfile/attribute/annotation/target/EmptyTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/EmptyTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/FormalParameterTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/FormalParameterTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/FormalParameterTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/FormalParameterTargetInfo.java index 07906f5a2..0a621e2d4 100644 --- a/src/proguard/classfile/attribute/annotation/target/FormalParameterTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/FormalParameterTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetElement.java b/core/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetElement.java similarity index 96% rename from src/proguard/classfile/attribute/annotation/target/LocalVariableTargetElement.java rename to core/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetElement.java index 85e3a689a..89d48fd2c 100644 --- a/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetElement.java +++ b/core/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetElement.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetInfo.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/target/LocalVariableTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetInfo.java index 7e532fcd7..b2678f627 100644 --- a/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/LocalVariableTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/OffsetTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/OffsetTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/OffsetTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/OffsetTargetInfo.java index 6eb7e9807..0fbf5cae2 100644 --- a/src/proguard/classfile/attribute/annotation/target/OffsetTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/OffsetTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/SuperTypeTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/SuperTypeTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/SuperTypeTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/SuperTypeTargetInfo.java index 6bb82f013..6927d89da 100644 --- a/src/proguard/classfile/attribute/annotation/target/SuperTypeTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/SuperTypeTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/TargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/TargetInfo.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/target/TargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/TargetInfo.java index 138942fc2..fa0e34f04 100644 --- a/src/proguard/classfile/attribute/annotation/target/TargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/TargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/ThrowsTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/ThrowsTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/ThrowsTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/ThrowsTargetInfo.java index c42a45b0d..725d3d934 100644 --- a/src/proguard/classfile/attribute/annotation/target/ThrowsTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/ThrowsTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/TypeArgumentTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/TypeArgumentTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/TypeArgumentTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/TypeArgumentTargetInfo.java index deb6eaabe..34078cc9b 100644 --- a/src/proguard/classfile/attribute/annotation/target/TypeArgumentTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/TypeArgumentTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/TypeParameterBoundTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/TypeParameterBoundTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/TypeParameterBoundTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/TypeParameterBoundTargetInfo.java index 6c628adeb..36e636131 100644 --- a/src/proguard/classfile/attribute/annotation/target/TypeParameterBoundTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/TypeParameterBoundTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/TypeParameterTargetInfo.java b/core/src/proguard/classfile/attribute/annotation/target/TypeParameterTargetInfo.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/target/TypeParameterTargetInfo.java rename to core/src/proguard/classfile/attribute/annotation/target/TypeParameterTargetInfo.java index 06f2b57bc..c08a67a68 100644 --- a/src/proguard/classfile/attribute/annotation/target/TypeParameterTargetInfo.java +++ b/core/src/proguard/classfile/attribute/annotation/target/TypeParameterTargetInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/visitor/LocalVariableTargetElementVisitor.java b/core/src/proguard/classfile/attribute/annotation/target/visitor/LocalVariableTargetElementVisitor.java similarity index 96% rename from src/proguard/classfile/attribute/annotation/target/visitor/LocalVariableTargetElementVisitor.java rename to core/src/proguard/classfile/attribute/annotation/target/visitor/LocalVariableTargetElementVisitor.java index 659a0ff8b..43b050720 100644 --- a/src/proguard/classfile/attribute/annotation/target/visitor/LocalVariableTargetElementVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/target/visitor/LocalVariableTargetElementVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/target/visitor/TargetInfoVisitor.java b/core/src/proguard/classfile/attribute/annotation/target/visitor/TargetInfoVisitor.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/target/visitor/TargetInfoVisitor.java rename to core/src/proguard/classfile/attribute/annotation/target/visitor/TargetInfoVisitor.java index fef99f172..29e560642 100644 --- a/src/proguard/classfile/attribute/annotation/target/visitor/TargetInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/target/visitor/TargetInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java b/core/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java similarity index 99% rename from src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java rename to core/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java index cead20c1d..cd52e0204 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java b/core/src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java similarity index 99% rename from src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java rename to core/src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java index 3e9bf0f81..18478561d 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/AllElementValueVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java b/core/src/proguard/classfile/attribute/annotation/visitor/AnnotationToAnnotatedClassVisitor.java similarity index 91% rename from src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java rename to core/src/proguard/classfile/attribute/annotation/visitor/AnnotationToAnnotatedClassVisitor.java index 646b5ab6b..5703b7110 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/AnnotationToAnnotatedClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -33,7 +33,7 @@ * * @author Eric Lafortune */ -public class AnnotatedClassVisitor +public class AnnotationToAnnotatedClassVisitor extends SimplifiedVisitor implements AnnotationVisitor { @@ -42,7 +42,7 @@ public class AnnotatedClassVisitor private Clazz lastVisitedClass; - public AnnotatedClassVisitor(ClassVisitor classVisitor) + public AnnotationToAnnotatedClassVisitor(ClassVisitor classVisitor) { this.classVisitor = classVisitor; } diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java b/core/src/proguard/classfile/attribute/annotation/visitor/AnnotationToAnnotatedMemberVisitor.java similarity index 91% rename from src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java rename to core/src/proguard/classfile/attribute/annotation/visitor/AnnotationToAnnotatedMemberVisitor.java index c21dcd6a5..1a30c79fa 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/AnnotationToAnnotatedMemberVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -33,7 +33,7 @@ * * @author Eric Lafortune */ -public class AnnotationToMemberVisitor +public class AnnotationToAnnotatedMemberVisitor extends SimplifiedVisitor implements AnnotationVisitor { @@ -42,7 +42,7 @@ public class AnnotationToMemberVisitor private Member lastVisitedMember; - public AnnotationToMemberVisitor(MemberVisitor memberVisitor) + public AnnotationToAnnotatedMemberVisitor(MemberVisitor memberVisitor) { this.memberVisitor = memberVisitor; } diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java b/core/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java similarity index 62% rename from src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java rename to core/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java index 0d5a6e611..d5247f8cc 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,6 +25,8 @@ import proguard.classfile.attribute.annotation.Annotation; import proguard.util.*; +import java.util.List; + /** * This AnnotationVisitor delegates its visits to another given * AnnotationVisitor, but only when the visited annotation has @@ -40,16 +42,49 @@ public class AnnotationTypeFilter /** - * 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. + * Creates a new AnnotationTypeFilter. + * @param regularExpression the regular expression against which + * annotation type names will be matched. + * @param annotationVisitor the annotation visitor to which visits + * will be delegated. + */ + public AnnotationTypeFilter(String regularExpression, + AnnotationVisitor annotationVisitor) + { + this(regularExpression, null, annotationVisitor); + } + + + /** + * Creates a new AnnotationTypeFilter. + * @param regularExpression the regular expression against which + * annotation type names will be matched. + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + * @param annotationVisitor the annotation visitor to which visits + * will be delegated. */ public AnnotationTypeFilter(String regularExpression, + List variableStringMatchers, + AnnotationVisitor annotationVisitor) + { + this(new ListParser(new ClassNameParser()).parse(regularExpression), + annotationVisitor); + } + + + /** + * Creates a new AnnotationTypeFilter. + * @param regularExpressionMatcher the string matcher against which + * class names will be matched. + * @param annotationVisitor the annotation visitor to which visits + * will be delegated. + */ + public AnnotationTypeFilter(StringMatcher regularExpressionMatcher, AnnotationVisitor annotationVisitor) { - this.regularExpressionMatcher = new ListParser(new ClassNameParser()).parse(regularExpression); + this.regularExpressionMatcher = regularExpressionMatcher; this.annotationVisitor = annotationVisitor; } diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java b/core/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java rename to core/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java index b9c93e98b..e1c3a8fac 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java b/core/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java similarity index 98% rename from src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java rename to core/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java index 076c0421f..7864b213f 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/visitor/TypeAnnotationVisitor.java b/core/src/proguard/classfile/attribute/annotation/visitor/TypeAnnotationVisitor.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/visitor/TypeAnnotationVisitor.java rename to core/src/proguard/classfile/attribute/annotation/visitor/TypeAnnotationVisitor.java index 3a2f4fd8f..c7c63fa84 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/TypeAnnotationVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/TypeAnnotationVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/visitor/TypePathInfoVisitor.java b/core/src/proguard/classfile/attribute/annotation/visitor/TypePathInfoVisitor.java similarity index 97% rename from src/proguard/classfile/attribute/annotation/visitor/TypePathInfoVisitor.java rename to core/src/proguard/classfile/attribute/annotation/visitor/TypePathInfoVisitor.java index 34bfa081d..62c04ef57 100644 --- a/src/proguard/classfile/attribute/annotation/visitor/TypePathInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/annotation/visitor/TypePathInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/annotation/visitor/package.html b/core/src/proguard/classfile/attribute/annotation/visitor/package.html similarity index 100% rename from src/proguard/classfile/attribute/annotation/visitor/package.html rename to core/src/proguard/classfile/attribute/annotation/visitor/package.html diff --git a/core/src/proguard/classfile/attribute/module/ExportsInfo.java b/core/src/proguard/classfile/attribute/module/ExportsInfo.java new file mode 100644 index 000000000..6599c2783 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/ExportsInfo.java @@ -0,0 +1,106 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * Representation of a Exports entry in a Module attribute. + * + * @author Joachim Vandersmissen + */ +public class ExportsInfo implements VisitorAccepter +{ + public int u2exportsIndex; + public int u2exportsFlags; + public int u2exportsToCount; + public int[] u2exportsToIndex; + + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized ExportsInfo. + */ + public ExportsInfo() + { + } + + + /** + * Creates an initialized ExportsInfo. + */ + public ExportsInfo(int u2exportsIndex, + int u2exportsFlags, + int u2exportsToCount, + int[] u2exportsToIndex) + { + this.u2exportsIndex = u2exportsIndex; + this.u2exportsFlags = u2exportsFlags; + this.u2exportsToCount = u2exportsToCount; + this.u2exportsToIndex = u2exportsToIndex; + } + + + /** + * Applies the given constant pool visitor to the package constant of the + * package, if any. + */ + public void packageAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2exportsIndex != 0) + { + clazz.constantPoolEntryAccept(u2exportsIndex, constantVisitor); + } + } + + + /** + * Applies the given constant pool visitor to all exportsToIndex. + */ + public void exportsToIndexAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + // Loop over all exportsToIndex. + for (int index = 0; index < u2exportsToCount; index++) + { + clazz.constantPoolEntryAccept(u2exportsToIndex[index], constantVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/core/src/proguard/classfile/attribute/module/ModuleAttribute.java b/core/src/proguard/classfile/attribute/module/ModuleAttribute.java new file mode 100644 index 000000000..84a9efb55 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/ModuleAttribute.java @@ -0,0 +1,195 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.module.visitor.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Attribute represents a module attribute. + * + * @author Joachim Vandersmissen + */ +public class ModuleAttribute extends Attribute +{ + public int u2moduleNameIndex; + public int u2moduleFlags; + public int u2moduleVersionIndex; + public int u2requiresCount; + public RequiresInfo[] requires; + public int u2exportsCount; + public ExportsInfo[] exports; + public int u2opensCount; + public OpensInfo[] opens; + public int u2usesCount; + public int[] u2uses; + public int u2providesCount; + public ProvidesInfo[] provides; + + + /** + * Creates an uninitialized ModuleAttribute. + */ + public ModuleAttribute() + { + } + + + /** + * Creates an initialized ModuleAttribute. + */ + public ModuleAttribute(int u2attributeNameIndex, + int u2moduleNameIndex, + int u2moduleFlags, + int u2moduleVersionIndex, + int u2requiresCount, + RequiresInfo[] requires, + int u2exportsCount, + ExportsInfo[] exports, + int u2opensCount, + OpensInfo[] opens, + int u2usesCount, + int[] u2uses, + int u2ProvidesCount, + ProvidesInfo[] provides) + { + super(u2attributeNameIndex); + + this.u2moduleNameIndex = u2moduleNameIndex; + this.u2moduleFlags = u2moduleFlags; + this.u2moduleVersionIndex = u2moduleVersionIndex; + this.u2requiresCount = u2requiresCount; + this.requires = requires; + this.u2exportsCount = u2exportsCount; + this.exports = exports; + this.u2opensCount = u2opensCount; + this.opens = opens; + this.u2usesCount = u2usesCount; + this.u2uses = u2uses; + this.u2providesCount = u2ProvidesCount; + this.provides = provides; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitModuleAttribute(clazz, this); + } + + + /** + * Applies the given constant pool visitor to the Utf8 constant of the name, + * if any. + */ + public void nameAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2moduleNameIndex != 0) + { + clazz.constantPoolEntryAccept(u2moduleNameIndex, constantVisitor); + } + } + + + /** + * Applies the given constant pool visitor to the Utf8 constant of the + * version, if any. + */ + public void versionAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2moduleVersionIndex != 0) + { + clazz.constantPoolEntryAccept(u2moduleVersionIndex, constantVisitor); + } + } + + + /** + * Applies the given visitor to all requires. + */ + public void requiresAccept(Clazz clazz, RequiresInfoVisitor requiresInfoVisitor) + { + for (int index = 0; index < u2requiresCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of RequiresInfo. + requiresInfoVisitor.visitRequiresInfo(clazz, requires[index]); + } + } + + + /** + * Applies the given visitor to all exports. + */ + public void exportsAccept(Clazz clazz, ExportsInfoVisitor exportsInfoVisitor) + { + for (int index = 0; index < u2exportsCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of ExportsInfo. + exportsInfoVisitor.visitExportsInfo(clazz, exports[index]); + } + } + + + /** + * Applies the given visitor to all exports. + */ + public void opensAccept(Clazz clazz, OpensInfoVisitor opensInfoVisitor) + { + for (int index = 0; index < u2opensCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of OpensInfo. + opensInfoVisitor.visitOpensInfo(clazz, opens[index]); + } + } + + + /** + * Applies the given constant pool visitor to all uses. + */ + public void usesAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + for (int index = 0; index < u2usesCount; index++) + { + clazz.constantPoolEntryAccept(u2uses[index], constantVisitor); + } + } + + + /** + * Applies the given visitor to all provides. + */ + public void providesAccept(Clazz clazz, ProvidesInfoVisitor providesInfoVisitor) + { + for (int index = 0; index < u2providesCount; index++) + { + // We don't need double dispatching here, since there is only one + // type of ProvidesInfo. + providesInfoVisitor.visitProvidesInfo(clazz, provides[index]); + } + } +} diff --git a/core/src/proguard/classfile/attribute/module/ModuleMainClassAttribute.java b/core/src/proguard/classfile/attribute/module/ModuleMainClassAttribute.java new file mode 100644 index 000000000..c929c391c --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/ModuleMainClassAttribute.java @@ -0,0 +1,76 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This Attribute represents a main class attribute. + * + * @author Joachim Vandersmissen + */ +public class ModuleMainClassAttribute extends Attribute +{ + public int u2mainClass; + + + /** + * Creates an uninitialized ModuleMainClassAttribute. + */ + public ModuleMainClassAttribute() + { + } + + + /** + * Creates an initialized ModuleMainClassAttribute. + */ + public ModuleMainClassAttribute(int u2attributeNameIndex, int u2mainClass) + { + super(u2attributeNameIndex); + this.u2mainClass = u2mainClass; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitModuleMainClassAttribute(clazz, this); + } + + + /** + * Applies the given constant pool visitor to the class constant of the + * main class, if any. + */ + public void mainClassAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2mainClass != 0) + { + clazz.constantPoolEntryAccept(u2mainClass, constantVisitor); + } + } +} diff --git a/core/src/proguard/classfile/attribute/module/ModulePackagesAttribute.java b/core/src/proguard/classfile/attribute/module/ModulePackagesAttribute.java new file mode 100644 index 000000000..6a01f3662 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/ModulePackagesAttribute.java @@ -0,0 +1,79 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This Attribute represents a module packages attribute. + * + * @author Joachim Vandersmissen + */ +public class ModulePackagesAttribute extends Attribute +{ + public int u2packagesCount; + public int[] u2packages; + + + /** + * Creates an uninitialized ModulePackagesAttribute. + */ + public ModulePackagesAttribute() + { + } + + + /** + * Creates an initialized ModulePackagesAttribute. + */ + public ModulePackagesAttribute(int u2attributeNameIndex, + int u2packagesCount, + int[] u2packages) + { + super(u2attributeNameIndex); + this.u2packagesCount = u2packagesCount; + this.u2packages = u2packages; + } + + + // Implementations for Attribute. + + public void accept(Clazz clazz, AttributeVisitor attributeVisitor) + { + attributeVisitor.visitModulePackagesAttribute(clazz, this); + } + + + /** + * Applies the given constant pool visitor to all packages. + */ + public void packagesAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + // Loop over all packages. + for (int index = 0; index < u2packagesCount; index++) + { + clazz.constantPoolEntryAccept(u2packages[index], constantVisitor); + } + } +} diff --git a/core/src/proguard/classfile/attribute/module/OpensInfo.java b/core/src/proguard/classfile/attribute/module/OpensInfo.java new file mode 100644 index 000000000..70eb402c6 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/OpensInfo.java @@ -0,0 +1,106 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * Representation of a Opens entry in a Module attribute. + * + * @author Joachim Vandersmissen + */ +public class OpensInfo implements VisitorAccepter +{ + public int u2opensIndex; + public int u2opensFlags; + public int u2opensToCount; + public int[] u2opensToIndex; + + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized OpensInfo. + */ + public OpensInfo() + { + } + + + /** + * Creates an initialized OpensInfo. + */ + public OpensInfo(int u2opensIndex, + int u2opensFlags, + int u2opensToCount, + int[] u2opensToIndex) + { + this.u2opensIndex = u2opensIndex; + this.u2opensFlags = u2opensFlags; + this.u2opensToCount = u2opensToCount; + this.u2opensToIndex = u2opensToIndex; + } + + + /** + * Applies the given constant pool visitor to the package constant of the + * package, if any. + */ + public void packageAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2opensIndex != 0) + { + clazz.constantPoolEntryAccept(u2opensIndex, constantVisitor); + } + } + + + /** + * Applies the given constant pool visitor to all targets. + */ + public void targetsAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + // Loop over all targets. + for (int index = 0; index < u2opensToCount; index++) + { + clazz.constantPoolEntryAccept(u2opensToIndex[index], constantVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/core/src/proguard/classfile/attribute/module/ProvidesInfo.java b/core/src/proguard/classfile/attribute/module/ProvidesInfo.java new file mode 100644 index 000000000..c1a04274b --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/ProvidesInfo.java @@ -0,0 +1,103 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * Representation of a Provides entry in a Module attribute. + * + * @author Joachim Vandersmissen + */ +public class ProvidesInfo implements VisitorAccepter +{ + public int u2providesIndex; + public int u2providesWithCount; + public int[] u2providesWithIndex; + + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized ProvidesInfo. + */ + public ProvidesInfo() + { + } + + + /** + * Creates an initialized ProvidesInfo. + */ + public ProvidesInfo(int u2providesIndex, + int u2providesWithCount, + int[] u2providesWithIndex) + { + this.u2providesIndex = u2providesIndex; + this.u2providesWithCount = u2providesWithCount; + this.u2providesWithIndex = u2providesWithIndex; + } + + + /** + * Applies the given constant pool visitor to the class constant of the + * provides, if any. + */ + public void providesAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2providesIndex != 0) + { + clazz.constantPoolEntryAccept(u2providesIndex, constantVisitor); + } + } + + + /** + * Applies the given constant pool visitor to all with entries. + */ + public void withAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + // Loop over all u2providesWithIndex entries. + for (int index = 0; index < u2providesWithCount; index++) + { + clazz.constantPoolEntryAccept(u2providesWithIndex[index], constantVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/core/src/proguard/classfile/attribute/module/RequiresInfo.java b/core/src/proguard/classfile/attribute/module/RequiresInfo.java new file mode 100644 index 000000000..b3b86ca85 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/RequiresInfo.java @@ -0,0 +1,102 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module; + +import proguard.classfile.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * Representation of a Requires entry in a Module attribute. + * + * @author Joachim Vandersmissen + */ +public class RequiresInfo implements VisitorAccepter +{ + public int u2requiresIndex; + public int u2requiresFlags; + public int u2requiresVersionIndex; + + + /** + * An extra field in which visitors can store information. + */ + public Object visitorInfo; + + + /** + * Creates an uninitialized RequiresInfo. + */ + public RequiresInfo() + { + } + + + /** + * Creates an uninitialized RequiresInfo. + */ + public RequiresInfo(int u2requiresIndex, + int u2requiresFlags, + int u2requiresVersionIndex) + { + this.u2requiresIndex = u2requiresIndex; + this.u2requiresFlags = u2requiresFlags; + this.u2requiresVersionIndex = u2requiresVersionIndex; + } + + + /** + * Applies the given constant pool visitor to the module constant of the + * module, if any. + */ + public void moduleAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2requiresIndex != 0) + { + clazz.constantPoolEntryAccept(u2requiresIndex, constantVisitor); + } + } + + /** + * Applies the given constant pool visitor to the Utf8 constant of the + * version, if any. + */ + public void versionAccept(Clazz clazz, ConstantVisitor constantVisitor) + { + if (u2requiresVersionIndex != 0) + { + clazz.constantPoolEntryAccept(u2requiresVersionIndex, constantVisitor); + } + } + + + // Implementations for VisitorAccepter. + + public Object getVisitorInfo() + { + return visitorInfo; + } + + + public void setVisitorInfo(Object visitorInfo) + { + this.visitorInfo = visitorInfo; + } +} diff --git a/core/src/proguard/classfile/attribute/module/package.html b/core/src/proguard/classfile/attribute/module/package.html new file mode 100644 index 000000000..1e5d11b0a --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/package.html @@ -0,0 +1,4 @@ + +This package contains classes to represent the module attributes inside class +files. + diff --git a/core/src/proguard/classfile/attribute/module/visitor/AllExportsInfoVisitor.java b/core/src/proguard/classfile/attribute/module/visitor/AllExportsInfoVisitor.java new file mode 100644 index 000000000..5eef7d90f --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/AllExportsInfoVisitor.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.module.ModuleAttribute; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor lets a given ExportsInfoVisitor visit all + * ExportsInfo objects of the ModuleAttribute objects it visits. + * + * @author Joachim Vandersmissen + */ +public class AllExportsInfoVisitor +extends SimplifiedVisitor + implements AttributeVisitor +{ + private final ExportsInfoVisitor exportsInfoVisitor; + + + public AllExportsInfoVisitor(ExportsInfoVisitor exportsInfoVisitor) + { + this.exportsInfoVisitor = exportsInfoVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + moduleAttribute.exportsAccept(clazz, exportsInfoVisitor); + } +} diff --git a/core/src/proguard/classfile/attribute/module/visitor/AllOpensInfoVisitor.java b/core/src/proguard/classfile/attribute/module/visitor/AllOpensInfoVisitor.java new file mode 100644 index 000000000..65fe4f2f8 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/AllOpensInfoVisitor.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.module.ModuleAttribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor lets a given OpensInfoVisitor visit all + * OpensInfo objects of the ModuleAttribute objects it visits. + * + * @author Joachim Vandersmissen + */ +public class AllOpensInfoVisitor +extends SimplifiedVisitor + implements AttributeVisitor +{ + private final OpensInfoVisitor opensInfoVisitor; + + + public AllOpensInfoVisitor(OpensInfoVisitor opensInfoVisitor) + { + this.opensInfoVisitor = opensInfoVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + moduleAttribute.opensAccept(clazz, opensInfoVisitor); + } +} diff --git a/core/src/proguard/classfile/attribute/module/visitor/AllProvidesInfoVisitor.java b/core/src/proguard/classfile/attribute/module/visitor/AllProvidesInfoVisitor.java new file mode 100644 index 000000000..fd7834cca --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/AllProvidesInfoVisitor.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.module.ModuleAttribute; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor lets a given ProvidesInfoVisitor visit all + * ProvidesInfo objects of the ModuleAttribute objects it visits. + * + * @author Joachim Vandersmissen + */ +public class AllProvidesInfoVisitor +extends SimplifiedVisitor + implements AttributeVisitor +{ + private final ProvidesInfoVisitor providesInfoVisitor; + + + public AllProvidesInfoVisitor(ProvidesInfoVisitor providesInfoVisitor) + { + this.providesInfoVisitor = providesInfoVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + moduleAttribute.providesAccept(clazz, providesInfoVisitor); + } +} diff --git a/core/src/proguard/classfile/attribute/module/visitor/AllRequiresInfoVisitor.java b/core/src/proguard/classfile/attribute/module/visitor/AllRequiresInfoVisitor.java new file mode 100644 index 000000000..1dbe261c4 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/AllRequiresInfoVisitor.java @@ -0,0 +1,57 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.module.ModuleAttribute; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This AttributeVisitor lets a given RequiresInfoVisitor visit all + * RequiresInfo objects of the ModuleAttribute objects it visits. + * + * @author Joachim Vandersmissen + */ +public class AllRequiresInfoVisitor +extends SimplifiedVisitor + implements AttributeVisitor +{ + private final RequiresInfoVisitor requiresInfoVisitor; + + + public AllRequiresInfoVisitor(RequiresInfoVisitor requiresInfoVisitor) + { + this.requiresInfoVisitor = requiresInfoVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + moduleAttribute.requiresAccept(clazz, requiresInfoVisitor); + } +} diff --git a/core/src/proguard/classfile/attribute/module/visitor/ExportsInfoVisitor.java b/core/src/proguard/classfile/attribute/module/visitor/ExportsInfoVisitor.java new file mode 100644 index 000000000..a57a6d4d9 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/ExportsInfoVisitor.java @@ -0,0 +1,37 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.module.ExportsInfo; + +/** + * This interface specifies the methods for a visitor of + * ExportsInfo objects. Note that there is only a single + * implementation of ExportsInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Joachim Vandersmissen + */ +public interface ExportsInfoVisitor +{ + public void visitExportsInfo(Clazz clazz, ExportsInfo exportsInfo); +} diff --git a/core/src/proguard/classfile/attribute/module/visitor/OpensInfoVisitor.java b/core/src/proguard/classfile/attribute/module/visitor/OpensInfoVisitor.java new file mode 100644 index 000000000..551ee5ba1 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/OpensInfoVisitor.java @@ -0,0 +1,37 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.module.*; + +/** + * This interface specifies the methods for a visitor of + * OpensInfo objects. Note that there is only a single + * implementation of OpensInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Joachim Vandersmissen + */ +public interface OpensInfoVisitor +{ + public void visitOpensInfo(Clazz clazz, OpensInfo opensInfo); +} diff --git a/core/src/proguard/classfile/attribute/module/visitor/ProvidesInfoVisitor.java b/core/src/proguard/classfile/attribute/module/visitor/ProvidesInfoVisitor.java new file mode 100644 index 000000000..50e3e6452 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/ProvidesInfoVisitor.java @@ -0,0 +1,37 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.module.ProvidesInfo; + +/** + * This interface specifies the methods for a visitor of + * ProvidesInfo objects. Note that there is only a single + * implementation of ProvidesInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Joachim Vandersmissen + */ +public interface ProvidesInfoVisitor +{ + public void visitProvidesInfo(Clazz clazz, ProvidesInfo providesInfo); +} diff --git a/core/src/proguard/classfile/attribute/module/visitor/RequiresInfoVisitor.java b/core/src/proguard/classfile/attribute/module/visitor/RequiresInfoVisitor.java new file mode 100644 index 000000000..4bad50c68 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/RequiresInfoVisitor.java @@ -0,0 +1,37 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.module.RequiresInfo; + +/** + * This interface specifies the methods for a visitor of + * RequiresInfo objects. Note that there is only a single + * implementation of RequiresInfo, such that this interface + * is not strictly necessary as a visitor. + * + * @author Joachim Vandersmissen + */ +public interface RequiresInfoVisitor +{ + public void visitRequiresInfo(Clazz clazz, RequiresInfo requiresInfo); +} diff --git a/core/src/proguard/classfile/attribute/module/visitor/package.html b/core/src/proguard/classfile/attribute/module/visitor/package.html new file mode 100644 index 000000000..01537ff11 --- /dev/null +++ b/core/src/proguard/classfile/attribute/module/visitor/package.html @@ -0,0 +1,3 @@ + +This package contains visitors for module attributes and their components. + diff --git a/src/proguard/classfile/attribute/package.html b/core/src/proguard/classfile/attribute/package.html similarity index 100% rename from src/proguard/classfile/attribute/package.html rename to core/src/proguard/classfile/attribute/package.html diff --git a/src/proguard/classfile/attribute/preverification/DoubleType.java b/core/src/proguard/classfile/attribute/preverification/DoubleType.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/DoubleType.java rename to core/src/proguard/classfile/attribute/preverification/DoubleType.java index ff124d7b1..d76086c54 100644 --- a/src/proguard/classfile/attribute/preverification/DoubleType.java +++ b/core/src/proguard/classfile/attribute/preverification/DoubleType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/FloatType.java b/core/src/proguard/classfile/attribute/preverification/FloatType.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/FloatType.java rename to core/src/proguard/classfile/attribute/preverification/FloatType.java index 3a8e8315c..8cb66a884 100644 --- a/src/proguard/classfile/attribute/preverification/FloatType.java +++ b/core/src/proguard/classfile/attribute/preverification/FloatType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/FullFrame.java b/core/src/proguard/classfile/attribute/preverification/FullFrame.java similarity index 99% rename from src/proguard/classfile/attribute/preverification/FullFrame.java rename to core/src/proguard/classfile/attribute/preverification/FullFrame.java index 8c405d224..fe3c64f8f 100644 --- a/src/proguard/classfile/attribute/preverification/FullFrame.java +++ b/core/src/proguard/classfile/attribute/preverification/FullFrame.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/IntegerType.java b/core/src/proguard/classfile/attribute/preverification/IntegerType.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/IntegerType.java rename to core/src/proguard/classfile/attribute/preverification/IntegerType.java index 5228b61b4..ebdfb4cf2 100644 --- a/src/proguard/classfile/attribute/preverification/IntegerType.java +++ b/core/src/proguard/classfile/attribute/preverification/IntegerType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/LessZeroFrame.java b/core/src/proguard/classfile/attribute/preverification/LessZeroFrame.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/LessZeroFrame.java rename to core/src/proguard/classfile/attribute/preverification/LessZeroFrame.java index 430850638..055fcce10 100644 --- a/src/proguard/classfile/attribute/preverification/LessZeroFrame.java +++ b/core/src/proguard/classfile/attribute/preverification/LessZeroFrame.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/LongType.java b/core/src/proguard/classfile/attribute/preverification/LongType.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/LongType.java rename to core/src/proguard/classfile/attribute/preverification/LongType.java index 82b8af907..5cd4ae05e 100644 --- a/src/proguard/classfile/attribute/preverification/LongType.java +++ b/core/src/proguard/classfile/attribute/preverification/LongType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java b/core/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/MoreZeroFrame.java rename to core/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java index a8858048e..fef29c701 100644 --- a/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java +++ b/core/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/NullType.java b/core/src/proguard/classfile/attribute/preverification/NullType.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/NullType.java rename to core/src/proguard/classfile/attribute/preverification/NullType.java index 96003f067..658ca623b 100644 --- a/src/proguard/classfile/attribute/preverification/NullType.java +++ b/core/src/proguard/classfile/attribute/preverification/NullType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/ObjectType.java b/core/src/proguard/classfile/attribute/preverification/ObjectType.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/ObjectType.java rename to core/src/proguard/classfile/attribute/preverification/ObjectType.java index 5a4f1b775..8d6f3849a 100644 --- a/src/proguard/classfile/attribute/preverification/ObjectType.java +++ b/core/src/proguard/classfile/attribute/preverification/ObjectType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/SameOneFrame.java b/core/src/proguard/classfile/attribute/preverification/SameOneFrame.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/SameOneFrame.java rename to core/src/proguard/classfile/attribute/preverification/SameOneFrame.java index 8a1676270..d9f0b17ed 100644 --- a/src/proguard/classfile/attribute/preverification/SameOneFrame.java +++ b/core/src/proguard/classfile/attribute/preverification/SameOneFrame.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/SameZeroFrame.java b/core/src/proguard/classfile/attribute/preverification/SameZeroFrame.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/SameZeroFrame.java rename to core/src/proguard/classfile/attribute/preverification/SameZeroFrame.java index 90200ba03..82df6acaa 100644 --- a/src/proguard/classfile/attribute/preverification/SameZeroFrame.java +++ b/core/src/proguard/classfile/attribute/preverification/SameZeroFrame.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/StackMapAttribute.java b/core/src/proguard/classfile/attribute/preverification/StackMapAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/StackMapAttribute.java rename to core/src/proguard/classfile/attribute/preverification/StackMapAttribute.java index 5ff0a0832..3757c7c4b 100644 --- a/src/proguard/classfile/attribute/preverification/StackMapAttribute.java +++ b/core/src/proguard/classfile/attribute/preverification/StackMapAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -64,8 +64,6 @@ public StackMapAttribute(int stackMapFramesCount, } - - // Implementations for Attribute. public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor) diff --git a/src/proguard/classfile/attribute/preverification/StackMapFrame.java b/core/src/proguard/classfile/attribute/preverification/StackMapFrame.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/StackMapFrame.java rename to core/src/proguard/classfile/attribute/preverification/StackMapFrame.java index 474650cc2..62168fa62 100644 --- a/src/proguard/classfile/attribute/preverification/StackMapFrame.java +++ b/core/src/proguard/classfile/attribute/preverification/StackMapFrame.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java b/core/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java rename to core/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java index b122221e7..4daf61ea6 100644 --- a/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java +++ b/core/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/TopType.java b/core/src/proguard/classfile/attribute/preverification/TopType.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/TopType.java rename to core/src/proguard/classfile/attribute/preverification/TopType.java index 065c9ebc6..8258f2756 100644 --- a/src/proguard/classfile/attribute/preverification/TopType.java +++ b/core/src/proguard/classfile/attribute/preverification/TopType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/UninitializedThisType.java b/core/src/proguard/classfile/attribute/preverification/UninitializedThisType.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/UninitializedThisType.java rename to core/src/proguard/classfile/attribute/preverification/UninitializedThisType.java index 21d210be6..c19e43919 100644 --- a/src/proguard/classfile/attribute/preverification/UninitializedThisType.java +++ b/core/src/proguard/classfile/attribute/preverification/UninitializedThisType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/UninitializedType.java b/core/src/proguard/classfile/attribute/preverification/UninitializedType.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/UninitializedType.java rename to core/src/proguard/classfile/attribute/preverification/UninitializedType.java index ad44e05c7..4cba44893 100644 --- a/src/proguard/classfile/attribute/preverification/UninitializedType.java +++ b/core/src/proguard/classfile/attribute/preverification/UninitializedType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/VerificationType.java b/core/src/proguard/classfile/attribute/preverification/VerificationType.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/VerificationType.java rename to core/src/proguard/classfile/attribute/preverification/VerificationType.java index 29ff241fd..fcf977ef7 100644 --- a/src/proguard/classfile/attribute/preverification/VerificationType.java +++ b/core/src/proguard/classfile/attribute/preverification/VerificationType.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java b/core/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java similarity index 98% rename from src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java rename to core/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java index 3fca3d352..4e142d424 100644 --- a/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java +++ b/core/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java b/core/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java similarity index 97% rename from src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java rename to core/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java index 910bd3965..cd98800d9 100644 --- a/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java +++ b/core/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java b/core/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java similarity index 99% rename from src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java rename to core/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java index 3f087d580..dab527fae 100644 --- a/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java +++ b/core/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java b/core/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java similarity index 98% rename from src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java rename to core/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java index 62c53fb03..4e8b62708 100644 --- a/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java similarity index 97% rename from src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java index 11e892f68..e97510de8 100644 --- a/src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/AllBootstrapMethodInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java similarity index 96% rename from src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java index 1e4a6d6bf..6bfff5f9b 100644 --- a/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java similarity index 97% rename from src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java index 2c2d5603d..c31195fff 100644 --- a/src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/AllInnerClassesInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/AllLineNumberInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/AllLineNumberInfoVisitor.java similarity index 97% rename from src/proguard/classfile/attribute/visitor/AllLineNumberInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/AllLineNumberInfoVisitor.java index a3a37ba45..df128813a 100644 --- a/src/proguard/classfile/attribute/visitor/AllLineNumberInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/AllLineNumberInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/attribute/visitor/AttributeCounter.java b/core/src/proguard/classfile/attribute/visitor/AttributeCounter.java new file mode 100644 index 000000000..febdccd03 --- /dev/null +++ b/core/src/proguard/classfile/attribute/visitor/AttributeCounter.java @@ -0,0 +1,59 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.Attribute; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.util.Counter; + +/** + * This AttributeVisitor counts the number of attributes that have been visited. + * + * @author Thomas Neidhart + */ +public class AttributeCounter +extends SimplifiedVisitor +implements AttributeVisitor, + Counter +{ + private int count; + + + // Implementations for Counter. + + /** + * Returns the number of class members that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + count++; + } + +} diff --git a/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java b/core/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java similarity index 95% rename from src/proguard/classfile/attribute/visitor/AttributeNameFilter.java rename to core/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java index 340e4f223..81f61f9e3 100644 --- a/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java +++ b/core/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,7 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.module.*; import proguard.classfile.attribute.preverification.*; import proguard.util.*; @@ -143,6 +144,33 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + if (accepted(clazz, moduleAttribute)) + { + attributeVisitor.visitModuleAttribute(clazz, moduleAttribute); + } + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + if (accepted(clazz, moduleMainClassAttribute)) + { + attributeVisitor.visitModuleMainClassAttribute(clazz, moduleMainClassAttribute); + } + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + if (accepted(clazz, modulePackagesAttribute)) + { + attributeVisitor.visitModulePackagesAttribute(clazz, modulePackagesAttribute); + } + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { if (accepted(clazz, deprecatedAttribute)) diff --git a/core/src/proguard/classfile/attribute/visitor/AttributeToClassVisitor.java b/core/src/proguard/classfile/attribute/visitor/AttributeToClassVisitor.java new file mode 100644 index 000000000..6f1087f19 --- /dev/null +++ b/core/src/proguard/classfile/attribute/visitor/AttributeToClassVisitor.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.Attribute; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This AttributeVisitor delegates to a given ClassVisitor. + * + * @author Eric Lafortune + */ +public class AttributeToClassVisitor +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new AttributeToClassVisitor. + */ + public AttributeToClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + clazz.accept(classVisitor); + } +} diff --git a/src/proguard/classfile/attribute/visitor/AttributeVisitor.java b/core/src/proguard/classfile/attribute/visitor/AttributeVisitor.java similarity index 89% rename from src/proguard/classfile/attribute/visitor/AttributeVisitor.java rename to core/src/proguard/classfile/attribute/visitor/AttributeVisitor.java index 668d13a7a..e2c1c3815 100644 --- a/src/proguard/classfile/attribute/visitor/AttributeVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/AttributeVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,7 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.module.*; import proguard.classfile.attribute.preverification.*; /** @@ -35,15 +36,15 @@ 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 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); + public void visitModuleAttribute( Clazz clazz, ModuleAttribute moduleAttribute); + public void visitModuleMainClassAttribute( Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute); + public void visitModulePackagesAttribute( Clazz clazz, ModulePackagesAttribute modulePackagesAttribute); 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); @@ -98,4 +99,4 @@ public interface AttributeVisitor public void visitRuntimeInvisibleTypeAnnotationsAttribute( Clazz clazz, Method method, CodeAttribute codeAttribute, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute); public void visitAnnotationDefaultAttribute( Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute); -} +} \ No newline at end of file diff --git a/src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java old mode 100755 new mode 100644 similarity index 96% rename from src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java index f79683776..dbae62abe --- a/src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/BootstrapMethodInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/attribute/visitor/DebugAttributeVisitor.java b/core/src/proguard/classfile/attribute/visitor/DebugAttributeVisitor.java new file mode 100644 index 000000000..d6e198a2c --- /dev/null +++ b/core/src/proguard/classfile/attribute/visitor/DebugAttributeVisitor.java @@ -0,0 +1,591 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.module.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.visitor.SimpleClassPrinter; + +/** + * This AttributeVisitor delegates to a given AttributeVisitor, timing the + * invocations and printing out warnings when the timings exceed a given + * threshold. + * + * @author Eric Lafortune + */ +public class DebugAttributeVisitor +implements AttributeVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + public static boolean DEBUG = System.getProperty("dav") != null; + //*/ + + + private final String message; + private final long maximumTime; + private final AttributeVisitor attributeVisitor; + + + /** + * Creates a new DebugAttributeVisitor. + * @param attributeVisitor the AttributeVisitor to which visits will be + * delegated. + */ + public DebugAttributeVisitor(AttributeVisitor attributeVisitor) + { + this(attributeVisitor.getClass().getName(), + attributeVisitor); + } + + + /** + * Creates a new DebugAttributeVisitor. + * @param message the message to be printed when the maximum + * invocation time is exceeded. + * @param attributeVisitor the AttributeVisitor to which visits will be + * delegated. + */ + public DebugAttributeVisitor(String message, + AttributeVisitor attributeVisitor) + { + this(message, + 10000L, + attributeVisitor); + } + + + /** + * Creates a new DebugAttributeVisitor. + * @param message the message to be printed when the maximum + * invocation time is exceeded. + * @param maximumTime the maximum invocation time. + * @param attributeVisitor the AttributeVisitor to which visits will be + * delegated. + */ + public DebugAttributeVisitor(String message, + long maximumTime, + AttributeVisitor attributeVisitor) + { + String debugTime = System.getProperty("debug.time"); + + this.message = message; + this.maximumTime = debugTime != null ? Long.valueOf(debugTime) : maximumTime; + this.attributeVisitor = attributeVisitor; + } + + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + // Implementations for AttributeVisitor. + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitUnknownAttribute(clazz, unknownAttribute); + + checkTime(clazz, unknownAttribute, startTime); + } + + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitBootstrapMethodsAttribute(clazz, bootstrapMethodsAttribute); + + checkTime(clazz, bootstrapMethodsAttribute, startTime); + } + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitSourceFileAttribute(clazz, sourceFileAttribute); + + checkTime(clazz, sourceFileAttribute, startTime); + } + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitSourceDirAttribute(clazz, sourceDirAttribute); + + checkTime(clazz, sourceDirAttribute, startTime); + } + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitInnerClassesAttribute(clazz, innerClassesAttribute); + + checkTime(clazz, innerClassesAttribute, startTime); + } + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitEnclosingMethodAttribute(clazz, enclosingMethodAttribute); + + checkTime(clazz, enclosingMethodAttribute, startTime); + } + + + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitModuleAttribute(clazz, moduleAttribute); + + checkTime(clazz, moduleAttribute, startTime); + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitModuleMainClassAttribute(clazz, moduleMainClassAttribute); + + checkTime(clazz, moduleMainClassAttribute, startTime); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitModulePackagesAttribute(clazz, modulePackagesAttribute); + + checkTime(clazz, modulePackagesAttribute, startTime); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitDeprecatedAttribute(clazz, deprecatedAttribute); + + checkTime(clazz, deprecatedAttribute, startTime); + } + + public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitDeprecatedAttribute(clazz, field, deprecatedAttribute); + + checkTime(clazz, field, deprecatedAttribute, startTime); + } + + public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitDeprecatedAttribute(clazz, method, deprecatedAttribute); + + checkTime(clazz, method, deprecatedAttribute, startTime); + } + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitSyntheticAttribute(clazz, syntheticAttribute); + + checkTime(clazz, syntheticAttribute, startTime); + } + + public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitSyntheticAttribute(clazz, field, syntheticAttribute); + + checkTime(clazz, field, syntheticAttribute, startTime); + } + + public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitSyntheticAttribute(clazz, method, syntheticAttribute); + + checkTime(clazz, method, syntheticAttribute, startTime); + } + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitSignatureAttribute(clazz, signatureAttribute); + + checkTime(clazz, signatureAttribute, startTime); + } + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitSignatureAttribute(clazz, field, signatureAttribute); + + checkTime(clazz, field, signatureAttribute, startTime); + } + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitSignatureAttribute(clazz, method, signatureAttribute); + + checkTime(clazz, method, signatureAttribute, startTime); + } + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitConstantValueAttribute(clazz, field, constantValueAttribute); + + checkTime(clazz, field, constantValueAttribute, startTime); + } + + public void visitMethodParametersAttribute(Clazz clazz, Method method, MethodParametersAttribute methodParametersAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitMethodParametersAttribute(clazz, method, methodParametersAttribute); + + checkTime(clazz, method, methodParametersAttribute, startTime); + } + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitExceptionsAttribute(clazz, method, exceptionsAttribute); + + checkTime(clazz, method, exceptionsAttribute, startTime); + } + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitCodeAttribute(clazz, method, codeAttribute); + + checkTime(clazz, method, codeAttribute, startTime); + } + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitStackMapAttribute(clazz, method, codeAttribute, stackMapAttribute); + + checkTime(clazz, method, codeAttribute, startTime); + } + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitStackMapTableAttribute(clazz, method, codeAttribute, stackMapTableAttribute); + + checkTime(clazz, method, codeAttribute, startTime); + } + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitLineNumberTableAttribute(clazz, method, codeAttribute, lineNumberTableAttribute); + + checkTime(clazz, method, codeAttribute, startTime); + } + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitLocalVariableTableAttribute(clazz, method, codeAttribute, localVariableTableAttribute); + + checkTime(clazz, method, codeAttribute, startTime); + } + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, localVariableTypeTableAttribute); + + checkTime(clazz, method, codeAttribute, startTime); + } + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute); + + checkTime(clazz, runtimeVisibleAnnotationsAttribute, startTime); + } + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, field, runtimeVisibleAnnotationsAttribute); + + checkTime(clazz, field, runtimeVisibleAnnotationsAttribute, startTime); + } + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, method, runtimeVisibleAnnotationsAttribute); + + checkTime(clazz, method, runtimeVisibleAnnotationsAttribute, startTime); + } + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute); + + checkTime(clazz, runtimeInvisibleAnnotationsAttribute, startTime); + } + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, field, runtimeInvisibleAnnotationsAttribute); + + checkTime(clazz, field, runtimeInvisibleAnnotationsAttribute, startTime); + } + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, method, runtimeInvisibleAnnotationsAttribute); + + checkTime(clazz, method, runtimeInvisibleAnnotationsAttribute, startTime); + } + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeVisibleParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute); + + checkTime(clazz, method, runtimeVisibleParameterAnnotationsAttribute, startTime); + } + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeInvisibleParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute); + + checkTime(clazz, method, runtimeInvisibleParameterAnnotationsAttribute, startTime); + } + + + public void visitRuntimeVisibleTypeAnnotationsAttribute(Clazz clazz, RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeVisibleTypeAnnotationsAttribute(clazz, runtimeVisibleTypeAnnotationsAttribute); + + checkTime(clazz, runtimeVisibleTypeAnnotationsAttribute, startTime); + } + + public void visitRuntimeVisibleTypeAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeVisibleTypeAnnotationsAttribute(clazz, field, runtimeVisibleTypeAnnotationsAttribute); + + checkTime(clazz, field, runtimeVisibleTypeAnnotationsAttribute, startTime); + } + + public void visitRuntimeVisibleTypeAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeVisibleTypeAnnotationsAttribute(clazz, method, runtimeVisibleTypeAnnotationsAttribute); + + checkTime(clazz, method, runtimeVisibleTypeAnnotationsAttribute, startTime); + } + + public void visitRuntimeVisibleTypeAnnotationsAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeVisibleTypeAnnotationsAttribute(clazz, method, codeAttribute, runtimeVisibleTypeAnnotationsAttribute); + + checkTime(clazz, method, runtimeVisibleTypeAnnotationsAttribute, startTime); + } + + public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeInvisibleTypeAnnotationsAttribute(clazz, runtimeInvisibleTypeAnnotationsAttribute); + + checkTime(clazz, runtimeInvisibleTypeAnnotationsAttribute, startTime); + } + + public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeInvisibleTypeAnnotationsAttribute(clazz, field, runtimeInvisibleTypeAnnotationsAttribute); + + checkTime(clazz, field, runtimeInvisibleTypeAnnotationsAttribute, startTime); + } + + public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeInvisibleTypeAnnotationsAttribute(clazz, method, runtimeInvisibleTypeAnnotationsAttribute); + + checkTime(clazz, method, runtimeInvisibleTypeAnnotationsAttribute, startTime); + } + + public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitRuntimeInvisibleTypeAnnotationsAttribute(clazz, method, codeAttribute, runtimeInvisibleTypeAnnotationsAttribute); + + checkTime(clazz, method, runtimeInvisibleTypeAnnotationsAttribute, startTime); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + long startTime = startTime(); + + attributeVisitor.visitAnnotationDefaultAttribute(clazz, method, annotationDefaultAttribute); + + checkTime(clazz, method, annotationDefaultAttribute, startTime); + } + + + // Small utility methods. + + /** + * Returns the current time. + */ + private long startTime() + { + return DEBUG ? System.currentTimeMillis() : 0L; + } + + + /** + * Checks the time after having visited the given attribute, and prints + * out a message if necessary. + */ + private void checkTime(Clazz clazz, + Attribute attribute, + long startTime) + { + if (DEBUG) + { + long endTime = startTime(); + + long deltaTime = endTime - startTime; + + if (deltaTime > maximumTime) + { + System.err.println("=== " + message + " took "+((double)deltaTime/1000.)+" seconds ==="); + //attribute.accept(clazz, new ClassPrinter()); + System.err.println(); + } + } + } + + + /** + * Checks the time after having visited the given attribute, and prints + * out a message if necessary. + */ + private void checkTime(Clazz clazz, + Field field, + Attribute attribute, + long startTime) + { + if (DEBUG) + { + long endTime = startTime(); + + long deltaTime = endTime - startTime; + + if (deltaTime > maximumTime) + { + System.err.println("=== " + message + " took "+((double)deltaTime/1000.)+" seconds ==="); + field.accept(clazz, new SimpleClassPrinter(true)); + System.err.println(); + } + } + } + + + /** + * Checks the time after having visited the given attribute, and prints + * out a message if necessary. + */ + private void checkTime(Clazz clazz, + Method method, + Attribute attribute, + long startTime) + { + if (DEBUG) + { + long endTime = startTime(); + + long deltaTime = endTime - startTime; + + if (deltaTime > maximumTime) + { + System.err.println("=== " + message + " took "+((double)deltaTime/1000.)+" seconds ==="); + method.accept(clazz, new SimpleClassPrinter(true)); + System.err.println(); + } + } + } +} diff --git a/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java similarity index 96% rename from src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java index e15ab7cb1..80bbb7ceb 100644 --- a/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java similarity index 96% rename from src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java index d4e498493..49dfc75c9 100644 --- a/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/attribute/visitor/InstructionToAttributeVisitor.java b/core/src/proguard/classfile/attribute/visitor/InstructionToAttributeVisitor.java new file mode 100644 index 000000000..c2b80c794 --- /dev/null +++ b/core/src/proguard/classfile/attribute/visitor/InstructionToAttributeVisitor.java @@ -0,0 +1,58 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.CodeAttribute; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; + +/** + * This InstructionVisitor delegates to a given AttributeVisitor. + * + * @author Johan Leys + */ +public class InstructionToAttributeVisitor +extends SimplifiedVisitor +implements InstructionVisitor +{ + private final AttributeVisitor attributeVisitor; + + + /** + * Creates a new InstructionToAttributeVisitor. + */ + public InstructionToAttributeVisitor(AttributeVisitor attributeVisitor) + { + this.attributeVisitor = attributeVisitor; + } + + + // Implementations for InstructionVisitor. + + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + codeAttribute.accept(clazz, method, attributeVisitor); + } +} + diff --git a/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java similarity index 96% rename from src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java index c58d11304..323cf1939 100644 --- a/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/LineNumberRangeFinder.java b/core/src/proguard/classfile/attribute/visitor/LineNumberRangeFinder.java similarity index 97% rename from src/proguard/classfile/attribute/visitor/LineNumberRangeFinder.java rename to core/src/proguard/classfile/attribute/visitor/LineNumberRangeFinder.java index 4cbbff0d7..e7ebc2438 100644 --- a/src/proguard/classfile/attribute/visitor/LineNumberRangeFinder.java +++ b/core/src/proguard/classfile/attribute/visitor/LineNumberRangeFinder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java similarity index 96% rename from src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java index 523c1922f..96c4d81af 100644 --- a/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java similarity index 96% rename from src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java index 870d34078..07b251837 100644 --- a/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java b/core/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java similarity index 80% rename from src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java rename to core/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java index 92698ef9c..58bef7997 100644 --- a/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,7 +23,9 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.module.*; import proguard.classfile.attribute.preverification.*; +import proguard.util.ArrayUtil; /** * This AttributeVisitor delegates all visits to each AttributeVisitor @@ -34,42 +36,28 @@ public class MultiAttributeVisitor implements AttributeVisitor { private AttributeVisitor[] attributeVisitors; + private int attributeVisitorCount; public MultiAttributeVisitor() { + this.attributeVisitors = new AttributeVisitor[16]; } - public MultiAttributeVisitor(AttributeVisitor[] attributeVisitors) + public MultiAttributeVisitor(AttributeVisitor... attributeVisitors) { - this.attributeVisitors = attributeVisitors; + this.attributeVisitors = attributeVisitors; + this.attributeVisitorCount = attributeVisitors.length; } 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; - } + attributeVisitors = + ArrayUtil.add(attributeVisitors, + attributeVisitorCount++, + attributeVisitor); } @@ -78,7 +66,7 @@ private void incrementArraySize() public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitUnknownAttribute(clazz, unknownAttribute); } @@ -87,7 +75,7 @@ public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitBootstrapMethodsAttribute(clazz, bootstrapMethodsAttribute); } @@ -96,7 +84,7 @@ public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribut public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitSourceFileAttribute(clazz, sourceFileAttribute); } @@ -105,7 +93,7 @@ public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFile public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitSourceDirAttribute(clazz, sourceDirAttribute); } @@ -114,7 +102,7 @@ public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAtt public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitInnerClassesAttribute(clazz, innerClassesAttribute); } @@ -123,16 +111,43 @@ public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerC public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitEnclosingMethodAttribute(clazz, enclosingMethodAttribute); } } - public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) { for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitModuleAttribute(clazz, moduleAttribute); + } + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitModuleMainClassAttribute(clazz, moduleMainClassAttribute); + } + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + for (int index = 0; index < attributeVisitors.length; index++) + { + attributeVisitors[index].visitModulePackagesAttribute(clazz, modulePackagesAttribute); + } + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitDeprecatedAttribute(clazz, deprecatedAttribute); } @@ -141,7 +156,7 @@ public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecated public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitSyntheticAttribute(clazz, syntheticAttribute); } @@ -150,7 +165,7 @@ public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAtt public void visitSignatureAttribute(Clazz clazz, SignatureAttribute syntheticAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitSignatureAttribute(clazz, syntheticAttribute); } @@ -159,7 +174,7 @@ public void visitSignatureAttribute(Clazz clazz, SignatureAttribute syntheticAtt public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitDeprecatedAttribute(clazz, field, deprecatedAttribute); } @@ -168,7 +183,7 @@ public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribu public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitSyntheticAttribute(clazz, field, syntheticAttribute); } @@ -177,7 +192,7 @@ public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute syntheticAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitSignatureAttribute(clazz, field, syntheticAttribute); } @@ -186,7 +201,7 @@ public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitDeprecatedAttribute(clazz, method, deprecatedAttribute); } @@ -195,7 +210,7 @@ public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttri public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitSyntheticAttribute(clazz, method, syntheticAttribute); } @@ -204,7 +219,7 @@ public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribu public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute syntheticAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitSignatureAttribute(clazz, method, syntheticAttribute); } @@ -213,7 +228,7 @@ public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribu public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitConstantValueAttribute(clazz, field, constantValueAttribute); } @@ -231,7 +246,7 @@ public void visitMethodParametersAttribute(Clazz clazz, Method method, MethodPar public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitExceptionsAttribute(clazz, method, exceptionsAttribute); } @@ -240,7 +255,7 @@ public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttri public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitCodeAttribute(clazz, method, codeAttribute); } @@ -249,7 +264,7 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitStackMapAttribute(clazz, method, codeAttribute, stackMapAttribute); } @@ -258,7 +273,7 @@ public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute cod public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitStackMapTableAttribute(clazz, method, codeAttribute, stackMapTableAttribute); } @@ -267,7 +282,7 @@ public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribut public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitLineNumberTableAttribute(clazz, method, codeAttribute, lineNumberTableAttribute); } @@ -276,7 +291,7 @@ public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttrib public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitLocalVariableTableAttribute(clazz, method, codeAttribute, localVariableTableAttribute); } @@ -285,7 +300,7 @@ public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAtt public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, localVariableTypeTableAttribute); } @@ -294,7 +309,7 @@ public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, Cod public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute); } @@ -303,7 +318,7 @@ public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleA public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute); } @@ -312,7 +327,7 @@ public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisi public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, field, runtimeVisibleAnnotationsAttribute); } @@ -321,7 +336,7 @@ public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, Ru public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, field, runtimeInvisibleAnnotationsAttribute); } @@ -330,7 +345,7 @@ public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, method, runtimeVisibleAnnotationsAttribute); } @@ -339,7 +354,7 @@ public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, method, runtimeInvisibleAnnotationsAttribute); } @@ -348,7 +363,7 @@ public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitRuntimeVisibleParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute); } @@ -357,7 +372,7 @@ public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitRuntimeInvisibleParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute); } @@ -438,7 +453,7 @@ public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, Method me public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) { - for (int index = 0; index < attributeVisitors.length; index++) + for (int index = 0; index < attributeVisitorCount; index++) { attributeVisitors[index].visitAnnotationDefaultAttribute(clazz, method, annotationDefaultAttribute); } diff --git a/src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java b/core/src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java similarity index 93% rename from src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java rename to core/src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java index 52bbae16d..d04f3ca1b 100644 --- a/src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java +++ b/core/src/proguard/classfile/attribute/visitor/NonEmptyAttributeFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,7 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.module.*; import proguard.classfile.attribute.preverification.*; /** @@ -93,6 +94,34 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + if (moduleAttribute.u2requiresCount > 0 || + moduleAttribute.u2exportsCount > 0 || + moduleAttribute.u2opensCount > 0 || + moduleAttribute.u2usesCount > 0 || + moduleAttribute.u2providesCount > 0) + { + attributeVisitor.visitModuleAttribute(clazz, moduleAttribute); + } + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + attributeVisitor.visitModuleMainClassAttribute(clazz, moduleMainClassAttribute); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + if (modulePackagesAttribute.u2packagesCount > 0) + { + attributeVisitor.visitModulePackagesAttribute(clazz, modulePackagesAttribute); + } + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { attributeVisitor.visitDeprecatedAttribute(clazz, deprecatedAttribute); diff --git a/src/proguard/classfile/attribute/visitor/ParameterInfoVisitor.java b/core/src/proguard/classfile/attribute/visitor/ParameterInfoVisitor.java similarity index 96% rename from src/proguard/classfile/attribute/visitor/ParameterInfoVisitor.java rename to core/src/proguard/classfile/attribute/visitor/ParameterInfoVisitor.java index 59a743027..f55d19a4b 100644 --- a/src/proguard/classfile/attribute/visitor/ParameterInfoVisitor.java +++ b/core/src/proguard/classfile/attribute/visitor/ParameterInfoVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java b/core/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java similarity index 94% rename from src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java rename to core/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java index 32f29bfdd..0b2c8fc9b 100644 --- a/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java +++ b/core/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,7 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.module.*; import proguard.classfile.attribute.preverification.*; import proguard.obfuscate.AttributeShrinker; @@ -127,6 +128,33 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + if (optionalAttributeVisitor != null) + { + optionalAttributeVisitor.visitModuleAttribute(clazz, moduleAttribute); + } + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + if (optionalAttributeVisitor != null) + { + optionalAttributeVisitor.visitModuleMainClassAttribute(clazz, moduleMainClassAttribute); + } + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + if (optionalAttributeVisitor != null) + { + optionalAttributeVisitor.visitModulePackagesAttribute(clazz, modulePackagesAttribute); + } + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { if (optionalAttributeVisitor != null) diff --git a/src/proguard/classfile/attribute/visitor/StackSizeComputer.java b/core/src/proguard/classfile/attribute/visitor/StackSizeComputer.java similarity index 99% rename from src/proguard/classfile/attribute/visitor/StackSizeComputer.java rename to core/src/proguard/classfile/attribute/visitor/StackSizeComputer.java index 009666c69..a2cf434d7 100644 --- a/src/proguard/classfile/attribute/visitor/StackSizeComputer.java +++ b/core/src/proguard/classfile/attribute/visitor/StackSizeComputer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -151,7 +151,7 @@ public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAt int codeLength = codeAttribute.u4codeLength; // Make sure the global arrays are sufficiently large. - evaluated = ArrayUtil.ensureArraySize(evaluated, codeLength, false); + evaluated = ArrayUtil.ensureArraySize(evaluated, codeLength, false); stackSizesBefore = ArrayUtil.ensureArraySize(stackSizesBefore, codeLength, 0); stackSizesAfter = ArrayUtil.ensureArraySize(stackSizesAfter, codeLength, 0); diff --git a/src/proguard/classfile/attribute/visitor/package.html b/core/src/proguard/classfile/attribute/visitor/package.html similarity index 100% rename from src/proguard/classfile/attribute/visitor/package.html rename to core/src/proguard/classfile/attribute/visitor/package.html diff --git a/src/proguard/classfile/constant/ClassConstant.java b/core/src/proguard/classfile/constant/ClassConstant.java similarity index 98% rename from src/proguard/classfile/constant/ClassConstant.java rename to core/src/proguard/classfile/constant/ClassConstant.java index b4993743d..a3d07909b 100644 --- a/src/proguard/classfile/constant/ClassConstant.java +++ b/core/src/proguard/classfile/constant/ClassConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/Constant.java b/core/src/proguard/classfile/constant/Constant.java similarity index 97% rename from src/proguard/classfile/constant/Constant.java rename to core/src/proguard/classfile/constant/Constant.java index 86acf9d36..44eb3c3c9 100644 --- a/src/proguard/classfile/constant/Constant.java +++ b/core/src/proguard/classfile/constant/Constant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/DoubleConstant.java b/core/src/proguard/classfile/constant/DoubleConstant.java similarity index 97% rename from src/proguard/classfile/constant/DoubleConstant.java rename to core/src/proguard/classfile/constant/DoubleConstant.java index a9eca1eef..45e0abe3c 100644 --- a/src/proguard/classfile/constant/DoubleConstant.java +++ b/core/src/proguard/classfile/constant/DoubleConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/FieldrefConstant.java b/core/src/proguard/classfile/constant/FieldrefConstant.java similarity index 97% rename from src/proguard/classfile/constant/FieldrefConstant.java rename to core/src/proguard/classfile/constant/FieldrefConstant.java index 6cc30a383..6e35254e6 100644 --- a/src/proguard/classfile/constant/FieldrefConstant.java +++ b/core/src/proguard/classfile/constant/FieldrefConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/FloatConstant.java b/core/src/proguard/classfile/constant/FloatConstant.java similarity index 97% rename from src/proguard/classfile/constant/FloatConstant.java rename to core/src/proguard/classfile/constant/FloatConstant.java index 8dbe933ea..2406b1db2 100644 --- a/src/proguard/classfile/constant/FloatConstant.java +++ b/core/src/proguard/classfile/constant/FloatConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/IntegerConstant.java b/core/src/proguard/classfile/constant/IntegerConstant.java similarity index 97% rename from src/proguard/classfile/constant/IntegerConstant.java rename to core/src/proguard/classfile/constant/IntegerConstant.java index 9c09149c6..28ffbe747 100644 --- a/src/proguard/classfile/constant/IntegerConstant.java +++ b/core/src/proguard/classfile/constant/IntegerConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/InterfaceMethodrefConstant.java b/core/src/proguard/classfile/constant/InterfaceMethodrefConstant.java similarity index 97% rename from src/proguard/classfile/constant/InterfaceMethodrefConstant.java rename to core/src/proguard/classfile/constant/InterfaceMethodrefConstant.java index 0436ffeb7..7e6ae6e80 100644 --- a/src/proguard/classfile/constant/InterfaceMethodrefConstant.java +++ b/core/src/proguard/classfile/constant/InterfaceMethodrefConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/InvokeDynamicConstant.java b/core/src/proguard/classfile/constant/InvokeDynamicConstant.java old mode 100755 new mode 100644 similarity index 98% rename from src/proguard/classfile/constant/InvokeDynamicConstant.java rename to core/src/proguard/classfile/constant/InvokeDynamicConstant.java index 26c46d3a8..e98e67e0f --- a/src/proguard/classfile/constant/InvokeDynamicConstant.java +++ b/core/src/proguard/classfile/constant/InvokeDynamicConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/LongConstant.java b/core/src/proguard/classfile/constant/LongConstant.java similarity index 97% rename from src/proguard/classfile/constant/LongConstant.java rename to core/src/proguard/classfile/constant/LongConstant.java index 977d013ef..7795495e8 100644 --- a/src/proguard/classfile/constant/LongConstant.java +++ b/core/src/proguard/classfile/constant/LongConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/MethodHandleConstant.java b/core/src/proguard/classfile/constant/MethodHandleConstant.java old mode 100755 new mode 100644 similarity index 98% rename from src/proguard/classfile/constant/MethodHandleConstant.java rename to core/src/proguard/classfile/constant/MethodHandleConstant.java index 950136a32..b8234170e --- a/src/proguard/classfile/constant/MethodHandleConstant.java +++ b/core/src/proguard/classfile/constant/MethodHandleConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/MethodTypeConstant.java b/core/src/proguard/classfile/constant/MethodTypeConstant.java similarity index 98% rename from src/proguard/classfile/constant/MethodTypeConstant.java rename to core/src/proguard/classfile/constant/MethodTypeConstant.java index eba5d1166..2edabc55c 100644 --- a/src/proguard/classfile/constant/MethodTypeConstant.java +++ b/core/src/proguard/classfile/constant/MethodTypeConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/MethodrefConstant.java b/core/src/proguard/classfile/constant/MethodrefConstant.java similarity index 97% rename from src/proguard/classfile/constant/MethodrefConstant.java rename to core/src/proguard/classfile/constant/MethodrefConstant.java index a3a0b7ca3..899747e0e 100644 --- a/src/proguard/classfile/constant/MethodrefConstant.java +++ b/core/src/proguard/classfile/constant/MethodrefConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/constant/ModuleConstant.java b/core/src/proguard/classfile/constant/ModuleConstant.java new file mode 100644 index 000000000..6340286d1 --- /dev/null +++ b/core/src/proguard/classfile/constant/ModuleConstant.java @@ -0,0 +1,74 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 module constant in the constant pool. + * + * @author Joachim Vandersmissen + */ +public class ModuleConstant extends Constant +{ + public int u2nameIndex; + + + /** + * Creates an uninitialized ModuleConstant. + */ + public ModuleConstant() + { + } + + + /** + * Creates a new ModuleConstant with the given name index. + * @param u2nameIndex the index of the name in the constant pool. + */ + public ModuleConstant(int u2nameIndex) + { + this.u2nameIndex = u2nameIndex; + } + + + /** + * Returns the name. + */ + public String getName(Clazz clazz) + { + return clazz.getString(u2nameIndex); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Module; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitModuleConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/NameAndTypeConstant.java b/core/src/proguard/classfile/constant/NameAndTypeConstant.java similarity index 98% rename from src/proguard/classfile/constant/NameAndTypeConstant.java rename to core/src/proguard/classfile/constant/NameAndTypeConstant.java index f52c3ebcd..4c3218a20 100644 --- a/src/proguard/classfile/constant/NameAndTypeConstant.java +++ b/core/src/proguard/classfile/constant/NameAndTypeConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/constant/PackageConstant.java b/core/src/proguard/classfile/constant/PackageConstant.java new file mode 100644 index 000000000..362fedfbc --- /dev/null +++ b/core/src/proguard/classfile/constant/PackageConstant.java @@ -0,0 +1,74 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 package constant in the constant pool. + * + * @author Joachim Vandersmissen + */ +public class PackageConstant extends Constant +{ + public int u2nameIndex; + + + /** + * Creates an uninitialized PackageConstant. + */ + public PackageConstant() + { + } + + + /** + * Creates a new PackageConstant with the given name index. + * @param u2nameIndex the index of the name in the constant pool. + */ + public PackageConstant(int u2nameIndex) + { + this.u2nameIndex = u2nameIndex; + } + + + /** + * Returns the name. + */ + public String getName(Clazz clazz) + { + return clazz.getString(u2nameIndex); + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_Package; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitPackageConstant(clazz, this); + } +} diff --git a/core/src/proguard/classfile/constant/PrimitiveArrayConstant.java b/core/src/proguard/classfile/constant/PrimitiveArrayConstant.java new file mode 100644 index 000000000..1bb602930 --- /dev/null +++ b/core/src/proguard/classfile/constant/PrimitiveArrayConstant.java @@ -0,0 +1,230 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.ClassConstants; +import proguard.classfile.Clazz; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.constant.visitor.PrimitiveArrayConstantElementVisitor; +import proguard.classfile.constant.visitor.PrimitiveArrayConstantVisitor; + +/** + * This unofficial Constant represents an array of primitives in the constant + * pool. It is not supported by any Java specification and therefore only for + * internal use. + * + * @author Eric Lafortune + */ +public class PrimitiveArrayConstant extends Constant +{ + public Object values; + + + /** + * Creates an uninitialized PrimitiveArrayConstant. + */ + public PrimitiveArrayConstant() + { + } + + + /** + * Creates a new PrimitiveArrayConstant with the given array of values. + */ + public PrimitiveArrayConstant(Object values) + { + this.values = values; + } + + + /** + * Returns the type of the elements of the primitive array. + */ + public char getPrimitiveType() + { + return values instanceof boolean[] ? ClassConstants.TYPE_BOOLEAN : + values instanceof byte[] ? ClassConstants.TYPE_BYTE : + values instanceof char[] ? ClassConstants.TYPE_CHAR : + values instanceof short[] ? ClassConstants.TYPE_SHORT : + values instanceof int[] ? ClassConstants.TYPE_INT : + values instanceof float[] ? ClassConstants.TYPE_FLOAT : + values instanceof long[] ? ClassConstants.TYPE_LONG : + values instanceof double[] ? ClassConstants.TYPE_DOUBLE : + 0; + } + + + /** + * Returns the length of the primitive array. + */ + public int getLength() + { + return values instanceof boolean[] ? ((boolean[])values).length : + values instanceof byte[] ? ((byte[] )values).length : + values instanceof char[] ? ((char[] )values).length : + values instanceof short[] ? ((short[] )values).length : + values instanceof int[] ? ((int[] )values).length : + values instanceof float[] ? ((float[] )values).length : + values instanceof long[] ? ((long[] )values).length : + values instanceof double[] ? ((double[] )values).length : + 0; + } + + + /** + * Returns the values. + */ + public Object getValues() + { + return values; + } + + + /** + * Applies the given PrimitiveArrayConstantVisitor to the primitive array. + */ + public void primitiveArrayAccept(Clazz clazz, PrimitiveArrayConstantVisitor primitiveArrayConstantVisitor) + { + // The primitive arrays themselves don't accept visitors, so we have to + // use instanceof tests. + if (values instanceof boolean[]) + { + primitiveArrayConstantVisitor.visitBooleanArrayConstant(clazz, this, (boolean[])values); + } + else if (values instanceof byte[]) + { + primitiveArrayConstantVisitor.visitByteArrayConstant(clazz, this, (byte[])values); + } + else if (values instanceof char[]) + { + primitiveArrayConstantVisitor.visitCharArrayConstant(clazz, this, (char[])values); + } + else if (values instanceof short[]) + { + primitiveArrayConstantVisitor.visitShortArrayConstant(clazz, this, (short[])values); + } + else if (values instanceof int[]) + { + primitiveArrayConstantVisitor.visitIntArrayConstant(clazz, this, (int[])values); + } + else if (values instanceof float[]) + { + primitiveArrayConstantVisitor.visitFloatArrayConstant(clazz, this, (float[])values); + } + else if (values instanceof long[]) + { + primitiveArrayConstantVisitor.visitLongArrayConstant(clazz, this, (long[])values); + } + else if (values instanceof double[]) + { + primitiveArrayConstantVisitor.visitDoubleArrayConstant(clazz, this, (double[])values); + } + } + + + /** + * Applies the given PrimitiveArrayConstantElementVisitor to all elements + * of the primitive array. + */ + public void primitiveArrayElementsAccept(Clazz clazz, PrimitiveArrayConstantElementVisitor primitiveArrayConstantElementVisitor) + { + // The primitive arrays themselves don't accept visitors, so we have to + // use instanceof tests. + if (values instanceof boolean[]) + { + boolean[] booleanValues = (boolean[])this.values; + for (int index = 0; index < booleanValues.length; index++) + { + primitiveArrayConstantElementVisitor.visitBooleanArrayConstantElement(clazz, this, index, booleanValues[index]); + } + } + else if (values instanceof byte[]) + { + byte[] byteValues = (byte[])this.values; + for (int index = 0; index < byteValues.length; index++) + { + primitiveArrayConstantElementVisitor.visitByteArrayConstantElement(clazz, this, index, byteValues[index]); + } + } + else if (values instanceof char[]) + { + char[] charValues = (char[])this.values; + for (int index = 0; index < charValues.length; index++) + { + primitiveArrayConstantElementVisitor.visitCharArrayConstantElement(clazz, this, index, charValues[index]); + } + } + else if (values instanceof short[]) + { + short[] shortValues = (short[])this.values; + for (int index = 0; index < shortValues.length; index++) + { + primitiveArrayConstantElementVisitor.visitShortArrayConstantElement(clazz, this, index, shortValues[index]); + } + } + else if (values instanceof int[]) + { + int[] intValues = (int[])this.values; + for (int index = 0; index < intValues.length; index++) + { + primitiveArrayConstantElementVisitor.visitIntArrayConstantElement(clazz, this, index, intValues[index]); + } + } + else if (values instanceof float[]) + { + float[] floatValues = (float[])this.values; + for (int index = 0; index < floatValues.length; index++) + { + primitiveArrayConstantElementVisitor.visitFloatArrayConstantElement(clazz, this, index, floatValues[index]); + } + } + else if (values instanceof long[]) + { + long[] longValues = (long[])this.values; + for (int index = 0; index < longValues.length; index++) + { + primitiveArrayConstantElementVisitor.visitLongArrayConstantElement(clazz, this, index, longValues[index]); + } + } + else if (values instanceof double[]) + { + double[] doubleValues = (double[])this.values; + for (int index = 0; index < doubleValues.length; index++) + { + primitiveArrayConstantElementVisitor.visitDoubleArrayConstantElement(clazz, this, index, doubleValues[index]); + } + } + } + + + // Implementations for Constant. + + public int getTag() + { + return ClassConstants.CONSTANT_PrimitiveArray; + } + + public void accept(Clazz clazz, ConstantVisitor constantVisitor) + { + constantVisitor.visitPrimitiveArrayConstant(clazz, this); + } +} diff --git a/src/proguard/classfile/constant/RefConstant.java b/core/src/proguard/classfile/constant/RefConstant.java similarity index 98% rename from src/proguard/classfile/constant/RefConstant.java rename to core/src/proguard/classfile/constant/RefConstant.java index 11c59348e..599c763d6 100644 --- a/src/proguard/classfile/constant/RefConstant.java +++ b/core/src/proguard/classfile/constant/RefConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/StringConstant.java b/core/src/proguard/classfile/constant/StringConstant.java similarity index 98% rename from src/proguard/classfile/constant/StringConstant.java rename to core/src/proguard/classfile/constant/StringConstant.java index 42509a1cd..ec7f7c933 100644 --- a/src/proguard/classfile/constant/StringConstant.java +++ b/core/src/proguard/classfile/constant/StringConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/Utf8Constant.java b/core/src/proguard/classfile/constant/Utf8Constant.java similarity index 99% rename from src/proguard/classfile/constant/Utf8Constant.java rename to core/src/proguard/classfile/constant/Utf8Constant.java index 8ef9d5ecb..ecc6da5a2 100644 --- a/src/proguard/classfile/constant/Utf8Constant.java +++ b/core/src/proguard/classfile/constant/Utf8Constant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/visitor/AllConstantVisitor.java b/core/src/proguard/classfile/constant/visitor/AllConstantVisitor.java similarity index 96% rename from src/proguard/classfile/constant/visitor/AllConstantVisitor.java rename to core/src/proguard/classfile/constant/visitor/AllConstantVisitor.java index 90d4124aa..549715148 100644 --- a/src/proguard/classfile/constant/visitor/AllConstantVisitor.java +++ b/core/src/proguard/classfile/constant/visitor/AllConstantVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/visitor/BootstrapMethodArgumentVisitor.java b/core/src/proguard/classfile/constant/visitor/BootstrapMethodArgumentVisitor.java similarity index 97% rename from src/proguard/classfile/constant/visitor/BootstrapMethodArgumentVisitor.java rename to core/src/proguard/classfile/constant/visitor/BootstrapMethodArgumentVisitor.java index 8bbb34c54..782521b45 100644 --- a/src/proguard/classfile/constant/visitor/BootstrapMethodArgumentVisitor.java +++ b/core/src/proguard/classfile/constant/visitor/BootstrapMethodArgumentVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java b/core/src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java similarity index 98% rename from src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java rename to core/src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java index 032dafb97..080b7d86c 100644 --- a/src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java +++ b/core/src/proguard/classfile/constant/visitor/BootstrapMethodHandleTraveler.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/constant/visitor/ConstantCounter.java b/core/src/proguard/classfile/constant/visitor/ConstantCounter.java new file mode 100644 index 000000000..343379099 --- /dev/null +++ b/core/src/proguard/classfile/constant/visitor/ConstantCounter.java @@ -0,0 +1,58 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.constant.Constant; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.util.Counter; + +/** + * This ConstantVisitor counts the number of constants that have been visited. + * + * @author Eric Lafortune + */ +public class ConstantCounter +extends SimplifiedVisitor +implements ConstantVisitor, + Counter +{ + private int count; + + + // Implementations for Counter. + + /** + * Returns the number of class members that has been visited so far. + */ + public int getCount() + { + return count; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + count++; + } +} diff --git a/src/proguard/classfile/constant/visitor/ConstantTagFilter.java b/core/src/proguard/classfile/constant/visitor/ConstantTagFilter.java similarity index 98% rename from src/proguard/classfile/constant/visitor/ConstantTagFilter.java rename to core/src/proguard/classfile/constant/visitor/ConstantTagFilter.java index 43a95f9f3..6c774e237 100644 --- a/src/proguard/classfile/constant/visitor/ConstantTagFilter.java +++ b/core/src/proguard/classfile/constant/visitor/ConstantTagFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/visitor/ConstantVisitor.java b/core/src/proguard/classfile/constant/visitor/ConstantVisitor.java similarity index 87% rename from src/proguard/classfile/constant/visitor/ConstantVisitor.java rename to core/src/proguard/classfile/constant/visitor/ConstantVisitor.java index 2e74e3bec..19b0e8916 100644 --- a/src/proguard/classfile/constant/visitor/ConstantVisitor.java +++ b/core/src/proguard/classfile/constant/visitor/ConstantVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -36,6 +36,7 @@ public interface ConstantVisitor public void visitLongConstant( Clazz clazz, LongConstant longConstant); public void visitFloatConstant( Clazz clazz, FloatConstant floatConstant); public void visitDoubleConstant( Clazz clazz, DoubleConstant doubleConstant); + public void visitPrimitiveArrayConstant( Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant); public void visitStringConstant( Clazz clazz, StringConstant stringConstant); public void visitUtf8Constant( Clazz clazz, Utf8Constant utf8Constant); public void visitInvokeDynamicConstant( Clazz clazz, InvokeDynamicConstant invokeDynamicConstant); @@ -46,4 +47,6 @@ public interface ConstantVisitor public void visitClassConstant( Clazz clazz, ClassConstant classConstant); public void visitMethodTypeConstant( Clazz clazz, MethodTypeConstant methodTypeConstant); public void visitNameAndTypeConstant( Clazz clazz, NameAndTypeConstant nameAndTypeConstant); + public void visitModuleConstant( Clazz clazz, ModuleConstant moduleConstant); + public void visitPackageConstant( Clazz clazz, PackageConstant packageConstant); } diff --git a/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java b/core/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java similarity index 97% rename from src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java rename to core/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java index b49c47816..609bc5d98 100644 --- a/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java +++ b/core/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/visitor/MethodrefTraveler.java b/core/src/proguard/classfile/constant/visitor/MethodrefTraveler.java similarity index 97% rename from src/proguard/classfile/constant/visitor/MethodrefTraveler.java rename to core/src/proguard/classfile/constant/visitor/MethodrefTraveler.java index 4d8b0c6bf..17135ceb9 100644 --- a/src/proguard/classfile/constant/visitor/MethodrefTraveler.java +++ b/core/src/proguard/classfile/constant/visitor/MethodrefTraveler.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/constant/visitor/PrimitiveArrayConstantElementVisitor.java b/core/src/proguard/classfile/constant/visitor/PrimitiveArrayConstantElementVisitor.java new file mode 100644 index 000000000..0dd58f2c4 --- /dev/null +++ b/core/src/proguard/classfile/constant/visitor/PrimitiveArrayConstantElementVisitor.java @@ -0,0 +1,42 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.PrimitiveArrayConstant; + +/** + * This interface specifies the methods for a visitor of primitive elements + * of the array of a PrimitiveArrayConstant. + * + * @author Eric Lafortune + */ +public interface PrimitiveArrayConstantElementVisitor +{ + public void visitBooleanArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, boolean value); + public void visitByteArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, byte value); + public void visitCharArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, char value); + public void visitShortArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, short value); + public void visitIntArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, int value); + public void visitFloatArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, float value); + public void visitLongArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, long value); + public void visitDoubleArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, double value); +} diff --git a/core/src/proguard/classfile/constant/visitor/PrimitiveArrayConstantVisitor.java b/core/src/proguard/classfile/constant/visitor/PrimitiveArrayConstantVisitor.java new file mode 100644 index 000000000..79d015d9b --- /dev/null +++ b/core/src/proguard/classfile/constant/visitor/PrimitiveArrayConstantVisitor.java @@ -0,0 +1,42 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.PrimitiveArrayConstant; + +/** + * This interface specifies the methods for a visitor of PrimitiveArrayConstant + * instances containing different types of arrays. + * + * @author Eric Lafortune + */ +public interface PrimitiveArrayConstantVisitor +{ + public void visitBooleanArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, boolean[] values); + public void visitByteArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, byte[] values); + public void visitCharArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, char[] values); + public void visitShortArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, short[] values); + public void visitIntArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int[] values); + public void visitFloatArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, float[] values); + public void visitLongArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, long[] values); + public void visitDoubleArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, double[] values); +} diff --git a/src/proguard/classfile/constant/visitor/SuperClassConstantVisitor.java b/core/src/proguard/classfile/constant/visitor/SuperClassConstantVisitor.java similarity index 97% rename from src/proguard/classfile/constant/visitor/SuperClassConstantVisitor.java rename to core/src/proguard/classfile/constant/visitor/SuperClassConstantVisitor.java index 7e826c91e..90ce7265e 100644 --- a/src/proguard/classfile/constant/visitor/SuperClassConstantVisitor.java +++ b/core/src/proguard/classfile/constant/visitor/SuperClassConstantVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/constant/visitor/package.html b/core/src/proguard/classfile/constant/visitor/package.html similarity index 100% rename from src/proguard/classfile/constant/visitor/package.html rename to core/src/proguard/classfile/constant/visitor/package.html diff --git a/src/proguard/classfile/editor/AccessFixer.java b/core/src/proguard/classfile/editor/AccessFixer.java similarity index 98% rename from src/proguard/classfile/editor/AccessFixer.java rename to core/src/proguard/classfile/editor/AccessFixer.java index c9669499d..20b60b830 100644 --- a/src/proguard/classfile/editor/AccessFixer.java +++ b/core/src/proguard/classfile/editor/AccessFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -253,7 +253,7 @@ public void visitProgramMember(ProgramClass programClass, ProgramMember programM /** * Returns whether the two given classes are in the same package. */ - private boolean inSamePackage(ProgramClass class1, Clazz class2) + private boolean inSamePackage(Clazz class1, Clazz class2) { return ClassUtil.internalPackageName(class1.getName()).equals( ClassUtil.internalPackageName(class2.getName())); diff --git a/src/proguard/classfile/editor/AnnotationAdder.java b/core/src/proguard/classfile/editor/AnnotationAdder.java similarity index 99% rename from src/proguard/classfile/editor/AnnotationAdder.java rename to core/src/proguard/classfile/editor/AnnotationAdder.java index 3cd4d2f21..89430c58d 100644 --- a/src/proguard/classfile/editor/AnnotationAdder.java +++ b/core/src/proguard/classfile/editor/AnnotationAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/AnnotationsAttributeEditor.java b/core/src/proguard/classfile/editor/AnnotationsAttributeEditor.java similarity index 97% rename from src/proguard/classfile/editor/AnnotationsAttributeEditor.java rename to core/src/proguard/classfile/editor/AnnotationsAttributeEditor.java index bdd08b525..35114c3e4 100644 --- a/src/proguard/classfile/editor/AnnotationsAttributeEditor.java +++ b/core/src/proguard/classfile/editor/AnnotationsAttributeEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/AttributeAdder.java b/core/src/proguard/classfile/editor/AttributeAdder.java similarity index 99% rename from src/proguard/classfile/editor/AttributeAdder.java rename to core/src/proguard/classfile/editor/AttributeAdder.java index 9c84df39d..cf2daf5b0 100644 --- a/src/proguard/classfile/editor/AttributeAdder.java +++ b/core/src/proguard/classfile/editor/AttributeAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/AttributeSorter.java b/core/src/proguard/classfile/editor/AttributeSorter.java similarity index 98% rename from src/proguard/classfile/editor/AttributeSorter.java rename to core/src/proguard/classfile/editor/AttributeSorter.java index 4da8dcd96..ce444ac05 100644 --- a/src/proguard/classfile/editor/AttributeSorter.java +++ b/core/src/proguard/classfile/editor/AttributeSorter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/AttributesEditor.java b/core/src/proguard/classfile/editor/AttributesEditor.java similarity index 99% rename from src/proguard/classfile/editor/AttributesEditor.java rename to core/src/proguard/classfile/editor/AttributesEditor.java index 9944549b4..8471854ab 100644 --- a/src/proguard/classfile/editor/AttributesEditor.java +++ b/core/src/proguard/classfile/editor/AttributesEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/BootstrapMethodInfoAdder.java b/core/src/proguard/classfile/editor/BootstrapMethodInfoAdder.java similarity index 98% rename from src/proguard/classfile/editor/BootstrapMethodInfoAdder.java rename to core/src/proguard/classfile/editor/BootstrapMethodInfoAdder.java index 05f0fcbb2..003c4d825 100644 --- a/src/proguard/classfile/editor/BootstrapMethodInfoAdder.java +++ b/core/src/proguard/classfile/editor/BootstrapMethodInfoAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/BootstrapMethodRemapper.java b/core/src/proguard/classfile/editor/BootstrapMethodRemapper.java similarity index 67% rename from src/proguard/classfile/editor/BootstrapMethodRemapper.java rename to core/src/proguard/classfile/editor/BootstrapMethodRemapper.java index c13310b3c..a97851075 100644 --- a/src/proguard/classfile/editor/BootstrapMethodRemapper.java +++ b/core/src/proguard/classfile/editor/BootstrapMethodRemapper.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,18 +21,9 @@ 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 ConstantVisitor remaps all possible indices of bootstrap methods @@ -44,16 +35,30 @@ public class BootstrapMethodRemapper extends SimplifiedVisitor implements ConstantVisitor { - private int[] constantIndexMap; + private int[] bootstrapMethodIndexMap; + + // Ignore (skip) lingering InvokeDynamic constants that + // refer to removed bootstrap methods. + private final boolean ignoreDanglingConstants; + + public BootstrapMethodRemapper() + { + this(false); + } + + public BootstrapMethodRemapper(boolean ignoreDanglingConstants) + { + this.ignoreDanglingConstants = ignoreDanglingConstants; + } /** * Sets the given mapping of old constant pool entry indexes to their new * indexes. */ - public void setConstantIndexMap(int[] constantIndexMap) + public void setBootstrapMethodIndexMap(int[] bootstrapMethodIndexMap) { - this.constantIndexMap = constantIndexMap; + this.bootstrapMethodIndexMap = bootstrapMethodIndexMap; } @@ -72,15 +77,22 @@ public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invoke // Small utility methods. /** - * Returns the new bootstrap method index of the entry at the + * Returns the latest bootstrap method index of the entry at the * given index. */ private int remapConstantIndex(int constantIndex) { - int remappedConstantIndex = constantIndexMap[constantIndex]; + int remappedConstantIndex = bootstrapMethodIndexMap[constantIndex]; if (remappedConstantIndex < 0) { - throw new IllegalArgumentException("Can't remap constant index ["+constantIndex+"]"); + if (ignoreDanglingConstants) + { + return constantIndex; + } + else + { + throw new IllegalArgumentException("Can't remap bootstrap method index ["+constantIndex+"]"); + } } return remappedConstantIndex; diff --git a/src/proguard/classfile/editor/BootstrapMethodsAttributeAdder.java b/core/src/proguard/classfile/editor/BootstrapMethodsAttributeAdder.java similarity index 98% rename from src/proguard/classfile/editor/BootstrapMethodsAttributeAdder.java rename to core/src/proguard/classfile/editor/BootstrapMethodsAttributeAdder.java index 5ac2281a2..9f64885a5 100644 --- a/src/proguard/classfile/editor/BootstrapMethodsAttributeAdder.java +++ b/core/src/proguard/classfile/editor/BootstrapMethodsAttributeAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/BootstrapMethodsAttributeEditor.java b/core/src/proguard/classfile/editor/BootstrapMethodsAttributeEditor.java similarity index 55% rename from src/proguard/classfile/editor/BootstrapMethodsAttributeEditor.java rename to core/src/proguard/classfile/editor/BootstrapMethodsAttributeEditor.java index c2ed13a0c..870762ba3 100644 --- a/src/proguard/classfile/editor/BootstrapMethodsAttributeEditor.java +++ b/core/src/proguard/classfile/editor/BootstrapMethodsAttributeEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,8 +24,8 @@ import proguard.util.ArrayUtil; /** - * This class can add bootstrap methods to a given bootstrap methods attribute. - * Bootstrap methods to be added must have been filled out beforehand. + * This class can add/remove bootstrap methods to/from a given bootstrap methods + * attribute. Bootstrap methods to be added must have been filled out beforehand. * * @author Eric Lafortune */ @@ -51,10 +51,41 @@ public BootstrapMethodsAttributeEditor(BootstrapMethodsAttribute targetBootstrap public int addBootstrapMethodInfo(BootstrapMethodInfo bootstrapMethodInfo) { targetBootstrapMethodsAttribute.bootstrapMethods = - (BootstrapMethodInfo[])ArrayUtil.add(targetBootstrapMethodsAttribute.bootstrapMethods, - targetBootstrapMethodsAttribute.u2bootstrapMethodsCount, - bootstrapMethodInfo); + ArrayUtil.add(targetBootstrapMethodsAttribute.bootstrapMethods, + targetBootstrapMethodsAttribute.u2bootstrapMethodsCount, + bootstrapMethodInfo); return targetBootstrapMethodsAttribute.u2bootstrapMethodsCount++; } + + + /** + * Removes the given bootstrap method from the bootstrap method attribute. + */ + public void removeBootstrapMethodInfo(BootstrapMethodInfo bootstrapMethodInfo) + { + ArrayUtil.remove(targetBootstrapMethodsAttribute.bootstrapMethods, + targetBootstrapMethodsAttribute.u2bootstrapMethodsCount--, + findBootstrapMethodInfoIndex(bootstrapMethodInfo)); + } + + + /** + * Finds the index of the given bootstrap method info in the target attribute. + */ + private int findBootstrapMethodInfoIndex(BootstrapMethodInfo bootstrapMethodInfo) + { + int methodsCount = targetBootstrapMethodsAttribute.u2bootstrapMethodsCount; + BootstrapMethodInfo[] methodInfos = targetBootstrapMethodsAttribute.bootstrapMethods; + + for (int index = 0; index < methodsCount; index++) + { + if (methodInfos[index].equals(bootstrapMethodInfo)) + { + return index; + } + } + + return methodsCount; + } } \ No newline at end of file diff --git a/core/src/proguard/classfile/editor/BootstrapMethodsAttributeShrinker.java b/core/src/proguard/classfile/editor/BootstrapMethodsAttributeShrinker.java new file mode 100644 index 000000000..7020f507e --- /dev/null +++ b/core/src/proguard/classfile/editor/BootstrapMethodsAttributeShrinker.java @@ -0,0 +1,339 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; +import proguard.classfile.VisitorAccepter; + +import java.util.Arrays; + +/** + * This ClassVisitor removes all unused entries from the bootstrap method attribute. + * + * If all bootstrap methods are removed, it also removes the BootstrapMethodsAttribute from + * the visited class. Additionally, the java/lang/MethodHandles$Lookup class will be + * removed from the InnerClasses attribute and the InnerClassesAttribute will be removed if + * it was the only entry. + * + * @author Tim Van Den Broecke + */ +public class BootstrapMethodsAttributeShrinker +extends SimplifiedVisitor +implements ClassVisitor, + + // Implementation interfaces. + MemberVisitor, + AttributeVisitor, + InstructionVisitor, + BootstrapMethodInfoVisitor +{ + // A visitor info flag to indicate the bootstrap method is being used. + private static final Object USED = new Object(); + + private int[] bootstrapMethodIndexMap = new int[ClassConstants.TYPICAL_BOOTSTRAP_METHODS_ATTRIBUTE_SIZE]; + private final BootstrapMethodRemapper bootstrapMethodRemapper = new BootstrapMethodRemapper(true); + + private int referencedBootstrapMethodIndex = -1; + private boolean modified = false; + + + // Implementations for ClassVisitor. + + @Override + public void visitLibraryClass(LibraryClass libaryClass) {} + + + @Override + public void visitProgramClass(ProgramClass programClass) + { + // Clear the fields from any previous runs. + modified = false; + bootstrapMethodIndexMap = new int[ClassConstants.TYPICAL_BOOTSTRAP_METHODS_ATTRIBUTE_SIZE]; + + // Remove any previous visitor info. + programClass.accept(new ClassCleaner()); + + // Mark the bootstrap methods referenced by invokeDynamic instructions. + programClass.methodsAccept(this); + + // Shrink the bootstrap methods attribute + programClass.attributesAccept(this); + + if (modified) + { + // Clean up dangling and freed up constants + programClass.accept(new ConstantPoolShrinker()); + } + } + + + // Implementations for MemberVisitor. + + @Override + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + programMethod.attributesAccept(programClass, this); + } + + + // Implementations for AttributeVisitor. + + @Override + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + @Override + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.instructionsAccept(clazz, method, this); + } + + + @Override + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + if (referencedBootstrapMethodIndex > -1) + { + // We're marking bootstrap methods + bootstrapMethodsAttribute.bootstrapMethodEntryAccept(clazz, referencedBootstrapMethodIndex, this); + } + else + { + // The bootstrap methods have been marked, so now we shrink the array of BootstrapMethodInfo objects. + int newBootstrapMethodsCount = + shrinkBootstrapMethodArray(bootstrapMethodsAttribute.bootstrapMethods, + bootstrapMethodsAttribute.u2bootstrapMethodsCount); + + if (newBootstrapMethodsCount < bootstrapMethodsAttribute.u2bootstrapMethodsCount) + { + modified = true; + + bootstrapMethodsAttribute.u2bootstrapMethodsCount = newBootstrapMethodsCount; + + if (bootstrapMethodsAttribute.u2bootstrapMethodsCount == 0) + { + // Remove the entire attribute. + AttributesEditor attributesEditor = new AttributesEditor((ProgramClass)clazz, false); + attributesEditor.deleteAttribute(ClassConstants.ATTR_BootstrapMethods); + + // Only bootstrap methods require the java/lang/MethodHandles$Lookup + // inner class, so we can remove it. + clazz.attributesAccept(new MethodHandlesLookupInnerClassRemover(attributesEditor)); + } + else + { + // Remap all constant pool references to remaining bootstrap methods. + bootstrapMethodRemapper.setBootstrapMethodIndexMap(bootstrapMethodIndexMap); + clazz.constantPoolEntriesAccept(bootstrapMethodRemapper); + } + } + } + } + + + // Implementations for InstructionVisitor. + + @Override + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + @Override + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + if (constantInstruction.opcode == InstructionConstants.OP_INVOKEDYNAMIC) + { + ProgramClass programClass = (ProgramClass)clazz; + + InvokeDynamicConstant invokeDynamicConstant = + (InvokeDynamicConstant)programClass.getConstant(constantInstruction.constantIndex); + + referencedBootstrapMethodIndex = invokeDynamicConstant.getBootstrapMethodAttributeIndex(); + + programClass.attributesAccept(this); + + referencedBootstrapMethodIndex = -1; + } + } + + + // Implementations for BootstrapMethodInfoVisitor. + + @Override + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + markAsUsed(bootstrapMethodInfo); + } + + + // Small utility methods. + + /** + * Marks the given visitor accepter as being used. + */ + private void markAsUsed(BootstrapMethodInfo bootstrapMethodInfo) + { + bootstrapMethodInfo.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 entries that are not marked as being used from the given + * array of bootstrap methods. Creates a map from the old indices to the + * new indices as a side effect. + * @return the new number of entries. + */ + private int shrinkBootstrapMethodArray(BootstrapMethodInfo[] bootstrapMethods, int length) + { + if (bootstrapMethodIndexMap.length < length) + { + bootstrapMethodIndexMap = new int[length]; + } + + int counter = 0; + + // Shift the used bootstrap methods together. + for (int index = 0; index < length; index++) + { + BootstrapMethodInfo bootstrapMethod = bootstrapMethods[index]; + + // Is the entry being used? + if (isUsed(bootstrapMethod)) + { + // Remember the new index. + bootstrapMethodIndexMap[index] = counter; + + // Shift the entry. + bootstrapMethods[counter++] = bootstrapMethod; + } + else + { + // Remember an invalid index. + bootstrapMethodIndexMap[index] = -1; + } + } + + // Clear the remaining bootstrap methods. + Arrays.fill(bootstrapMethods, counter, length, null); + + return counter; + } + + private class MethodHandlesLookupInnerClassRemover + extends SimplifiedVisitor + implements AttributeVisitor, + + // Implementation interfaces. + InnerClassesInfoVisitor + { + private static final String METHOD_HANDLES_CLASS = "java/lang/invoke/MethodHandles"; + + private final Object methodHandleLookupMarker = new Object(); + + private final AttributesEditor attributesEditor; + + public MethodHandlesLookupInnerClassRemover(AttributesEditor attributesEditor) + { + this.attributesEditor = attributesEditor; + } + + // Implementations for AttributeVisitor + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + // Mark inner class infos that refer to Lookup. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + + // Remove all marked inner classes. + InnerClassesAttributeEditor editor = + new InnerClassesAttributeEditor(innerClassesAttribute); + for (int index = innerClassesAttribute.u2classesCount - 1; index >= 0; index--) + { + InnerClassesInfo innerClassesInfo = innerClassesAttribute.classes[index]; + if (shouldBeRemoved(innerClassesInfo)) + { + editor.removeInnerClassesInfo(innerClassesInfo); + } + } + + // Remove the attribute if it is empty. + if (innerClassesAttribute.u2classesCount == 0) + { + attributesEditor.deleteAttribute(ClassConstants.ATTR_InnerClasses); + } + } + + + // Implementations for InnerClassesInfoVisitor. + + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { + ProgramClass programClass = (ProgramClass) clazz; + + ClassConstant innerClass = + (ClassConstant) programClass.getConstant(innerClassesInfo.u2innerClassIndex); + ClassConstant outerClass = + (ClassConstant) programClass.getConstant(innerClassesInfo.u2outerClassIndex); + + if (isMethodHandleClass(innerClass, clazz) || + isMethodHandleClass(outerClass, clazz)) + { + markForRemoval(innerClassesInfo); + } + } + + + // Small utility methods. + + private void markForRemoval(InnerClassesInfo innerClassesInfo) + { + innerClassesInfo.setVisitorInfo(methodHandleLookupMarker); + } + + private boolean shouldBeRemoved(InnerClassesInfo innerClassesInfo) + { + return innerClassesInfo.getVisitorInfo() == methodHandleLookupMarker; + } + + public boolean isMethodHandleClass(ClassConstant classConstant, Clazz clazz) + { + return classConstant != null && + classConstant.getName(clazz).startsWith(METHOD_HANDLES_CLASS); + } + } +} diff --git a/src/proguard/classfile/editor/BridgeMethodFixer.java b/core/src/proguard/classfile/editor/BridgeMethodFixer.java similarity index 98% rename from src/proguard/classfile/editor/BridgeMethodFixer.java rename to core/src/proguard/classfile/editor/BridgeMethodFixer.java index f58ee97a8..e11eea4ff 100644 --- a/src/proguard/classfile/editor/BridgeMethodFixer.java +++ b/core/src/proguard/classfile/editor/BridgeMethodFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ClassEditor.java b/core/src/proguard/classfile/editor/ClassEditor.java similarity index 99% rename from src/proguard/classfile/editor/ClassEditor.java rename to core/src/proguard/classfile/editor/ClassEditor.java index 086a0310f..aa0b82de6 100644 --- a/src/proguard/classfile/editor/ClassEditor.java +++ b/core/src/proguard/classfile/editor/ClassEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ClassElementSorter.java b/core/src/proguard/classfile/editor/ClassElementSorter.java similarity index 97% rename from src/proguard/classfile/editor/ClassElementSorter.java rename to core/src/proguard/classfile/editor/ClassElementSorter.java index a9cdf0c3a..249245376 100644 --- a/src/proguard/classfile/editor/ClassElementSorter.java +++ b/core/src/proguard/classfile/editor/ClassElementSorter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ClassMemberSorter.java b/core/src/proguard/classfile/editor/ClassMemberSorter.java similarity index 97% rename from src/proguard/classfile/editor/ClassMemberSorter.java rename to core/src/proguard/classfile/editor/ClassMemberSorter.java index 4efcf914a..281466b2e 100644 --- a/src/proguard/classfile/editor/ClassMemberSorter.java +++ b/core/src/proguard/classfile/editor/ClassMemberSorter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ClassReferenceFixer.java b/core/src/proguard/classfile/editor/ClassReferenceFixer.java similarity index 99% rename from src/proguard/classfile/editor/ClassReferenceFixer.java rename to core/src/proguard/classfile/editor/ClassReferenceFixer.java index ffe7aa8fa..fb68461c8 100644 --- a/src/proguard/classfile/editor/ClassReferenceFixer.java +++ b/core/src/proguard/classfile/editor/ClassReferenceFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/CodeAttributeComposer.java b/core/src/proguard/classfile/editor/CodeAttributeComposer.java similarity index 99% rename from src/proguard/classfile/editor/CodeAttributeComposer.java rename to core/src/proguard/classfile/editor/CodeAttributeComposer.java index a3de9a9d6..f8977f977 100644 --- a/src/proguard/classfile/editor/CodeAttributeComposer.java +++ b/core/src/proguard/classfile/editor/CodeAttributeComposer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/CodeAttributeEditor.java b/core/src/proguard/classfile/editor/CodeAttributeEditor.java similarity index 72% rename from src/proguard/classfile/editor/CodeAttributeEditor.java rename to core/src/proguard/classfile/editor/CodeAttributeEditor.java index b37470516..1d4ba36f4 100644 --- a/src/proguard/classfile/editor/CodeAttributeEditor.java +++ b/core/src/proguard/classfile/editor/CodeAttributeEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -34,12 +34,40 @@ import proguard.classfile.util.SimplifiedVisitor; import proguard.util.ArrayUtil; -import java.util.Arrays; +import java.util.*; /** * This AttributeVisitor accumulates specified changes to code, and then applies * these accumulated changes to the code attributes that it visits. * + * The class also supports labels ({@link #label()}) and exception handlers + * ({@link #catch_(int,int,int)}) in replacement sequences. They provide + * local branch offsets inside the replacement sequences + * ({@link Label#offset()}). For example, creating a replacement sequence + * with the help of {@link InstructionSequenceBuilder}: + * + * final CodeAttributeEditor.Label TRY_START = codeAttributeEditor.label(); + * final CodeAttributeEditor.Label TRY_END = codeAttributeEditor.label(); + * final CodeAttributeEditor.Label CATCH_END = codeAttributeEditor.label(); + * + * final CodeAttributeEditor.Label CATCH_EXCEPTION = + * codeAttributeEditor.catch_(TRY_START.offset(), + * TRY_END.offset(), + * constantPoolEditor.addClassConstant("java/lang/Exception", null)); + * + * Instructions[] replacementInstructions = builder + * .label(TRY_START) + * ...... + * .label(TRY_END) + * .goto_(CATCH_END.offset()) + * .catch_(CATCH_EXCEPTION) + * ...... + * .athrow() + * .label(CATCH_END) + * ...... + * .instructions(); + * + * * @author Eric Lafortune */ public class CodeAttributeEditor @@ -59,9 +87,11 @@ public class CodeAttributeEditor //* private static final boolean DEBUG = false; /*/ - public static boolean DEBUG = false; + public static boolean DEBUG = System.getProperty("cae") != null; //*/ + private static final int LABEL_FLAG = 0x20000000; + private final boolean updateFrameSizes; private final boolean shrinkInstructions; @@ -70,10 +100,14 @@ public class CodeAttributeEditor 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 Map labels = new HashMap(); + + /*private*/public Instruction[] preOffsetInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; + /*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; @@ -112,25 +146,31 @@ public CodeAttributeEditor(boolean updateFrameSizes, /** - * Resets the accumulated code changes. + * Resets the accumulated code changes for a given anticipated maximum + * code length. If necessary, the size may still be extended while + * editing the code, with {@link #extend(int)}. * @param codeLength the length of the code that will be edited next. */ public void reset(int codeLength) { + labels.clear(); + // 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]; + preOffsetInsertions = new Instruction[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); + Arrays.fill(preOffsetInsertions, 0, codeLength, null); + 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; @@ -149,17 +189,19 @@ 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); + preOffsetInsertions = ArrayUtil.extendArray(preOffsetInsertions, codeLength); + preInsertions = ArrayUtil.extendArray(preInsertions, codeLength); + replacements = ArrayUtil.extendArray(replacements, codeLength); + postInsertions = 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); + Arrays.fill(preOffsetInsertions, this.codeLength, codeLength, null); + 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; @@ -168,7 +210,36 @@ public void extend(int codeLength) /** * Remembers to place the given instruction right before the instruction - * at the given offset. + * at the given offset. Any branches to the existing instruction will + * land after the new instruction. Similarly, any try blocks that start at + * the existing instruction will not include the new instruction. However, + * any try blocks that end right before the existing instruction wil now + * include the new instruction. + * @param instructionOffset the offset of the instruction. + * @param instruction the new instruction. + */ + public void insertBeforeOffset(int instructionOffset, Instruction instruction) + { + if (instructionOffset < 0 || + instructionOffset >= codeLength) + { + throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); + } + + preOffsetInsertions[instructionOffset] = shrinkInstructions ? + instruction.shrink() : + instruction; + + modified = true; + simple = false; + } + + + /** + * Remembers to place the given instruction right before the instruction + * at the given offset. Any branches to the existing instruction will + * also go to the new instruction. Similarly, any try blocks that include + * the existing instruction will also include the new instruction. * @param instructionOffset the offset of the instruction. * @param instruction the new instruction. */ @@ -191,7 +262,39 @@ public void insertBeforeInstruction(int instructionOffset, Instruction instructi /** * Remembers to place the given instructions right before the instruction - * at the given offset. + * at the given offset. Any branches to the existing instruction will + * land after the new instructions. Similarly, any try blocks that start at + * the existing instruction will not include the new instructions. However, + * any try blocks that end right before the existing instruction wil now + * include the new instructions. + * @param instructionOffset the offset of the instruction. + * @param instructions the new instructions. + */ + public void insertBeforeOffset(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); + + preOffsetInsertions[instructionOffset] = shrinkInstructions ? + instruction.shrink() : + instruction; + + modified = true; + simple = false; + } + + + /** + * Remembers to place the given instructions right before the instruction + * at the given offset. Any branches to the existing instruction will + * also go to the new instructions. Similarly, any try blocks that include + * the existing instruction will also include the new instructions. * @param instructionOffset the offset of the instruction. * @param instructions the new instructions. */ @@ -358,10 +461,11 @@ public void clearModifications(int instructionOffset) throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); } - preInsertions[instructionOffset] = null; - replacements[instructionOffset] = null; - postInsertions[instructionOffset] = null; - deleted[instructionOffset] = false; + preOffsetInsertions[instructionOffset] = null; + preInsertions[instructionOffset] = null; + replacements[instructionOffset] = null; + postInsertions[instructionOffset] = null; + deleted[instructionOffset] = false; } @@ -380,9 +484,10 @@ public boolean isModified() */ public boolean isModified(int instructionOffset) { - return preInsertions[instructionOffset] != null || - replacements[instructionOffset] != null || - postInsertions[instructionOffset] != null || + return preOffsetInsertions[instructionOffset] != null || + preInsertions[instructionOffset] != null || + replacements[instructionOffset] != null || + postInsertions[instructionOffset] != null || deleted[instructionOffset]; } @@ -674,6 +779,13 @@ private int mapInstructions(byte[] oldCode, int oldLength) private void mapInstruction(int oldOffset, Instruction instruction) { + // Account for the pre-offset-inserted instruction, if any. + Instruction preOffsetInstruction = preOffsetInsertions[oldOffset]; + if (preOffsetInstruction != null) + { + newOffset += preOffsetInstruction.length(newOffset); + } + newInstructionOffsets[oldOffset] = newOffset; // Account for the pre-inserted instruction, if any. @@ -756,6 +868,19 @@ private void moveInstruction(Clazz clazz, int oldOffset, Instruction instruction) { + // Update and insert the pre-inserted instruction, if any. + Instruction preOffsetInstruction = preOffsetInsertions[oldOffset]; + if (preOffsetInstruction != null) + { + if (DEBUG) + { + System.out.println(" Pre-inserted ["+oldOffset+"] -> "+preOffsetInstruction.toString(newOffset)); + } + + // Update the instruction. + preOffsetInstruction.accept(clazz, method, codeAttribute, oldOffset, this); + } + // Update and insert the pre-inserted instruction, if any. Instruction preInstruction = preInsertions[oldOffset]; if (preInstruction != null) @@ -1097,7 +1222,12 @@ private int newBranchOffset(int oldInstructionOffset, int oldBranchOffset, int newInstructionOffset) { - return newInstructionOffset(oldInstructionOffset + oldBranchOffset) - + // Compute the old branch target. + // Pass a label offset unchanged. + int oldBranchTargetOffset = isLabel(oldBranchOffset) ? oldBranchOffset : + oldInstructionOffset + oldBranchOffset; + + return newInstructionOffset(oldBranchTargetOffset) - newInstructionOffset; } @@ -1108,6 +1238,21 @@ private int newBranchOffset(int oldInstructionOffset, */ private int newInstructionOffset(int oldInstructionOffset) { + // Special case: is it actually a label? + if (isLabel(oldInstructionOffset)) + { + // Retrieve the new offset from the label. + int labelIdentifier = labelIdentifier(oldInstructionOffset); + Label label = (Label)labels.get(labelIdentifier); + if (label == null) + { + throw new IllegalArgumentException("Reference to unknown label identifier ["+labelIdentifier+"]"); + } + + return label.newOffset; + } + + // Otherwise retrieve the new instruction offset. if (oldInstructionOffset < 0 || oldInstructionOffset > codeLength) { @@ -1166,8 +1311,8 @@ private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos, /** - * This instruction is a composite of other instructions, for local use - * inside the editor class only. + * This pseudo-instruction is a composite of other instructions, for local + * use inside the editor class only. */ private class CompositeInstruction extends Instruction @@ -1244,8 +1389,6 @@ public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int Instruction instruction = instructions[index]; instruction.accept(clazz, method, codeAttribute, offset, CodeAttributeEditor.this); - - offset += instruction.length(offset); } } @@ -1264,4 +1407,265 @@ public String toString() return stringBuffer.toString(); } } + + + // For convenience, we also define two pseudo-instructions, to conveniently + // mark local labels and create new exceptions handlers. + + /** + * Creates a new label that can be used as a pseudo-instruction to mark + * a local offset. Its offset can be used as a branch target in + * replacement instructions ({@link Label#offset()}). + */ + public Label label() + { + return label(labels.size()); + } + + + /** + * Creates a new label that can be used as a pseudo-instruction to mark + * a local offset. Its offset can be used as a branch target in + * replacement instructions ({@link Label#offset()}). + */ + public Label label(int identifier) + { + Label label = new Label(identifier); + + // Remember the label, so we can retrieve its offset later on. + labels.put(new Integer(identifier), label); + + return label; + } + + + /** + * Creates a new catch instance that can be used as a pseudo-instruction + * to mark the start of an exception handler. Its offset can be used as + * a branch target in replacement instructions ({@link Label#offset()}). + */ + public Label catch_(int startOffset, + int endOffset, + int catchType) + { + return catch_(labels.size(), + startOffset, + endOffset, + catchType); + } + + + /** + * Creates a new catch instance that can be used as a pseudo-instruction + * to mark the start of an exception handler. Its offset can be used as + * a branch target in replacement instructions ({@link Label#offset()}). + */ + public Label catch_(int identifier, + int startOffset, + int endOffset, + int catchType) + { + Label catch_ = new Catch(identifier, startOffset, endOffset, catchType); + + // Remember the label, so we can retrieve its offset later on. + labels.put(new Integer(identifier), catch_); + + return catch_; + } + + + /** + * Returns whether the given instruction offset actually represents a + * label (which contains the actual offset). + */ + private static boolean isLabel(int instructionOffset) + { + return (instructionOffset & 0xff000000) == LABEL_FLAG; + } + + + /** + * Returns the label identifier that corrresponds to the given + * instruction offset. + */ + private static int labelIdentifier(int instructionOffset) + { + return instructionOffset & ~LABEL_FLAG; + } + + + /** + * This pseudo-instruction represents a label that marks an instruction + * offset, for use in the context of the code attribute editor only. + */ + private static class Label + extends Instruction + { + protected final int identifier; + + private int newOffset; + + + /** + * Creates a new Label. + * @param identifier an identifier that can be chosen freely. + */ + public Label(int identifier) + { + this.identifier = identifier; + } + + + /** + * Returns the offset that can then be used as a branch target in + * other replacement instructions. + */ + public int offset() + { + return LABEL_FLAG | identifier; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + return this; + } + + + public void write(byte[] code, int offset) + { + } + + + protected void readInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't read label instruction"); + } + + + protected void writeInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't write label instruction"); + } + + + public int length(int offset) + { + // Remember the offset, so we can retrieve it later on. + newOffset = offset; + + return 0; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + if (instructionVisitor.getClass() != CodeAttributeEditor.class) + { + throw new UnsupportedOperationException("Unexpected visitor ["+instructionVisitor+"]"); + } + } + + + // Implementations for Object. + + public String toString() + { + return "label_"+offset(); + } + } + + + /** + * This pseudo-instruction represents an exception handler, + * for use in the context of the code attribute editor only. + */ + private static class Catch + extends Label + { + private final int startOfffset; + private final int endOffset; + private final int catchType; + + + /** + * Creates a new Catch instance. + * @param identifier an identifier that can be chosen freely. + * @param startOffset the start offset of the catch block. + * @param endOffset the end offset of the catch block. + * @param catchType the index of the catch type in the constant pool. + */ + public Catch(int identifier, + int startOffset, + int endOffset, + int catchType) + { + super(identifier); + + this.startOfffset = startOffset; + this.endOffset = endOffset; + this.catchType = catchType; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + return this; + } + + + public void write(byte[] code, int offset) + { + } + + + protected void readInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't read catch instruction"); + } + + + protected void writeInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't write catch instruction"); + } + + + public int length(int offset) + { + return super.length(offset); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + if (instructionVisitor.getClass() != CodeAttributeEditor.class) + { + throw new UnsupportedOperationException("Unexpected visitor ["+instructionVisitor+"]"); + } + + // Add the exception. Its offsets will still be updated later on, + // like any other exception. + new ExceptionInfoEditor(codeAttribute).prependException( + new ExceptionInfo(startOfffset, + endOffset, + offset(), + catchType)); + } + + + // Implementations for Object. + + public String toString() + { + return "catch " + + (isLabel(startOfffset) ? "label_" : "") + startOfffset + ", " + + (isLabel(endOffset) ? "label_" : "") + endOffset + ", #" + + catchType; + } + } } diff --git a/src/proguard/classfile/editor/CodeAttributeEditorResetter.java b/core/src/proguard/classfile/editor/CodeAttributeEditorResetter.java similarity index 97% rename from src/proguard/classfile/editor/CodeAttributeEditorResetter.java rename to core/src/proguard/classfile/editor/CodeAttributeEditorResetter.java index 8abb3f6ba..1d0e69203 100644 --- a/src/proguard/classfile/editor/CodeAttributeEditorResetter.java +++ b/core/src/proguard/classfile/editor/CodeAttributeEditorResetter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/editor/CompactCodeAttributeComposer.java b/core/src/proguard/classfile/editor/CompactCodeAttributeComposer.java new file mode 100644 index 000000000..03f82e77c --- /dev/null +++ b/core/src/proguard/classfile/editor/CompactCodeAttributeComposer.java @@ -0,0 +1,2075 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.Constant; +import proguard.classfile.instruction.*; +import proguard.classfile.util.*; + +import static proguard.classfile.ClassConstants.*; + +/** + * This AttributeVisitor accumulates instructions and exceptions, and then + * copies them into code attributes that it visits. + * + * @see CodeAttributeComposer + * + * @author Eric Lafortune + */ +public class CompactCodeAttributeComposer +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final ConstantPoolEditor constantPoolEditor; + private final CodeAttributeComposer codeAttributeComposer; + + + /** + * Creates a new CompactCodeAttributeComposer that doesn't allow external + * branch targets or exception handlers and that automatically shrinks + * instructions. + */ + public CompactCodeAttributeComposer(ProgramClass targetClass) + { + this(targetClass, false, false, true); + } + + + /** + * Creates a new CompactCodeAttributeComposer. + * @param allowExternalBranchTargets specifies whether branch targets + * can lie outside the code fragment + * of the branch instructions. + * @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 CompactCodeAttributeComposer(ProgramClass targetClass, + boolean allowExternalBranchTargets, + boolean allowExternalExceptionHandlers, + boolean shrinkInstructions) + { + constantPoolEditor = + new ConstantPoolEditor(targetClass); + + codeAttributeComposer = + new CodeAttributeComposer(allowExternalBranchTargets, + allowExternalExceptionHandlers, + shrinkInstructions); + } + + + /** + * Starts a new code definition. + */ + public CompactCodeAttributeComposer reset() + { + codeAttributeComposer.reset(); + + return this; + } + + + /** + * 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 CompactCodeAttributeComposer beginCodeFragment(int maximumCodeFragmentLength) + { + codeAttributeComposer.beginCodeFragment(maximumCodeFragmentLength); + + return this; + } + + + /** + * 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 CompactCodeAttributeComposer appendInstruction(int oldInstructionOffset, + Instruction instruction) + { + codeAttributeComposer.appendInstruction(oldInstructionOffset, instruction); + + return this; + } + + + /** + * 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 CompactCodeAttributeComposer appendLabel(int oldInstructionOffset) + { + codeAttributeComposer.appendLabel(oldInstructionOffset); + + return this; + } + + + /** + * Appends the given instruction without defined offsets. + * @param instructions the instructions to be appended. + */ + public CompactCodeAttributeComposer appendInstructions(Instruction[] instructions) + { + codeAttributeComposer.appendInstructions(instructions); + + return this; + } + + + /** + * 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 CompactCodeAttributeComposer appendInstruction(Instruction instruction) + { + codeAttributeComposer.appendInstruction(instruction); + + return this; + } + + + /** + * Appends the given exception to the exception table. + * @param exceptionInfo the exception to be appended. + */ + public CompactCodeAttributeComposer appendException(ExceptionInfo exceptionInfo) + { + codeAttributeComposer.appendException(exceptionInfo); + + return this; + } + + + /** + * Inserts the given line number at the appropriate position in the line + * number table. + * @param lineNumberInfo the line number to be inserted. + * @return the index where the line number was actually inserted. + */ + public int insertLineNumber(LineNumberInfo lineNumberInfo) + { + return codeAttributeComposer.insertLineNumber(lineNumberInfo); + } + + + /** + * Inserts the given line number at the appropriate position in the line + * number table. + * @param minimumIndex the minimum index where the line number may be + * inserted. + * @param lineNumberInfo the line number to be inserted. + * @return the index where the line number was inserted. + */ + public int insertLineNumber(int minimumIndex, LineNumberInfo lineNumberInfo) + { + return codeAttributeComposer.insertLineNumber(minimumIndex, lineNumberInfo); + } + + + /** + * Appends the given line number to the line number table. + * @param lineNumberInfo the line number to be appended. + */ + public CompactCodeAttributeComposer appendLineNumber(LineNumberInfo lineNumberInfo) + { + codeAttributeComposer.appendLineNumber(lineNumberInfo); + + return this; + } + + + /** + * Wraps up the current code fragment, continuing with the previous one on + * the stack. + */ + public CompactCodeAttributeComposer endCodeFragment() + { + codeAttributeComposer.endCodeFragment(); + + return this; + } + + + // Methods corresponding to the bytecode opcodes. + + public CompactCodeAttributeComposer nop() + { + return add(new SimpleInstruction(InstructionConstants.OP_NOP)); + } + + public CompactCodeAttributeComposer aconst_null() + { + return add(new SimpleInstruction(InstructionConstants.OP_ACONST_NULL)); + } + + public CompactCodeAttributeComposer iconst(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_0, constant)); + } + + public CompactCodeAttributeComposer iconst_m1() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_M1)); + } + + public CompactCodeAttributeComposer iconst_0() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_0)); + } + + public CompactCodeAttributeComposer iconst_1() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_1)); + } + + public CompactCodeAttributeComposer iconst_2() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_2)); + } + + public CompactCodeAttributeComposer iconst_3() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_3)); + } + + public CompactCodeAttributeComposer iconst_4() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_4)); + } + + public CompactCodeAttributeComposer iconst_5() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_5)); + } + + public CompactCodeAttributeComposer lconst(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_LCONST_0, constant)); + } + + public CompactCodeAttributeComposer lconst_0() + { + return add(new SimpleInstruction(InstructionConstants.OP_LCONST_0)); + } + + public CompactCodeAttributeComposer lconst_1() + { + return add(new SimpleInstruction(InstructionConstants.OP_LCONST_1)); + } + + public CompactCodeAttributeComposer fconst(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_FCONST_0, constant)); + } + + public CompactCodeAttributeComposer fconst_0() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCONST_0)); + } + + public CompactCodeAttributeComposer fconst_1() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCONST_1)); + } + + public CompactCodeAttributeComposer fconst_2() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCONST_2)); + } + + public CompactCodeAttributeComposer dconst(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_DCONST_0, constant)); + } + + public CompactCodeAttributeComposer dconst_0() + { + return add(new SimpleInstruction(InstructionConstants.OP_DCONST_0)); + } + + public CompactCodeAttributeComposer dconst_1() + { + return add(new SimpleInstruction(InstructionConstants.OP_DCONST_1)); + } + + public CompactCodeAttributeComposer bipush(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_BIPUSH, constant)); + } + + public CompactCodeAttributeComposer sipush(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_SIPUSH, constant)); + } + + public CompactCodeAttributeComposer ldc(int value) + { + return ldc_(constantPoolEditor.addIntegerConstant(value)); + } + + public CompactCodeAttributeComposer ldc(float value) + { + return ldc_(constantPoolEditor.addFloatConstant(value)); + } + + public CompactCodeAttributeComposer ldc(String string) + { + return ldc(string, null, null); + } + + public CompactCodeAttributeComposer ldc(Object primitiveArray) + { + return ldc_(constantPoolEditor.addPrimitiveArrayConstant(primitiveArray)); + } + + public CompactCodeAttributeComposer ldc(String string, Clazz referencedClass, Method referencedMember) + { + return ldc_(constantPoolEditor.addStringConstant(string, referencedClass, referencedMember)); + } + + public CompactCodeAttributeComposer ldc(String className, Clazz referencedClass) + { + return ldc_(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public CompactCodeAttributeComposer ldc_(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_LDC, constantIndex)); + } + + public CompactCodeAttributeComposer ldc_w(int value) + { + return ldc_w_(constantPoolEditor.addIntegerConstant(value)); + } + + public CompactCodeAttributeComposer ldc_w(float value) + { + return ldc_w_(constantPoolEditor.addFloatConstant(value)); + } + + public CompactCodeAttributeComposer ldc_w(String string) + { + return ldc_w(string, null, null); + } + + public CompactCodeAttributeComposer ldc_w(String string, Clazz referencedClass, Method referencedMember) + { + return ldc_w_(constantPoolEditor.addStringConstant(string, referencedClass, referencedMember)); + } + + public CompactCodeAttributeComposer ldc_w(String className, Clazz referencedClass) + { + return ldc_w_(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public CompactCodeAttributeComposer ldc_w_(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_LDC_W, constantIndex)); + } + + public CompactCodeAttributeComposer ldc2_w(long value) + { + return ldc2_w(constantPoolEditor.addLongConstant(value)); + } + + public CompactCodeAttributeComposer ldc2_w(double value) + { + return ldc2_w(constantPoolEditor.addDoubleConstant(value)); + } + + public CompactCodeAttributeComposer ldc2_w(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_LDC2_W, constantIndex)); + } + + public CompactCodeAttributeComposer iload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD, variableIndex)); + } + + public CompactCodeAttributeComposer lload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD, variableIndex)); + } + + public CompactCodeAttributeComposer fload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD, variableIndex)); + } + + public CompactCodeAttributeComposer dload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD, variableIndex)); + } + + public CompactCodeAttributeComposer aload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD, variableIndex)); + } + + public CompactCodeAttributeComposer iload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD_0)); + } + + public CompactCodeAttributeComposer iload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD_1)); + } + + public CompactCodeAttributeComposer iload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD_2)); + } + + public CompactCodeAttributeComposer iload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD_3)); + } + + public CompactCodeAttributeComposer lload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD_0)); + } + + public CompactCodeAttributeComposer lload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD_1)); + } + + public CompactCodeAttributeComposer lload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD_2)); + } + + public CompactCodeAttributeComposer lload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD_3)); + } + + public CompactCodeAttributeComposer fload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD_0)); + } + + public CompactCodeAttributeComposer fload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD_1)); + } + + public CompactCodeAttributeComposer fload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD_2)); + } + + public CompactCodeAttributeComposer fload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD_3)); + } + + public CompactCodeAttributeComposer dload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD_0)); + } + + public CompactCodeAttributeComposer dload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD_1)); + } + + public CompactCodeAttributeComposer dload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD_2)); + } + + public CompactCodeAttributeComposer dload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD_3)); + } + + public CompactCodeAttributeComposer aload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD_0)); + } + + public CompactCodeAttributeComposer aload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD_1)); + } + + public CompactCodeAttributeComposer aload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD_2)); + } + + public CompactCodeAttributeComposer aload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD_3)); + } + + public CompactCodeAttributeComposer iaload() + { + return add(new SimpleInstruction(InstructionConstants.OP_IALOAD)); + } + + public CompactCodeAttributeComposer laload() + { + return add(new SimpleInstruction(InstructionConstants.OP_LALOAD)); + } + + public CompactCodeAttributeComposer faload() + { + return add(new SimpleInstruction(InstructionConstants.OP_FALOAD)); + } + + public CompactCodeAttributeComposer daload() + { + return add(new SimpleInstruction(InstructionConstants.OP_DALOAD)); + } + + public CompactCodeAttributeComposer aaload() + { + return add(new SimpleInstruction(InstructionConstants.OP_AALOAD)); + } + + public CompactCodeAttributeComposer baload() + { + return add(new SimpleInstruction(InstructionConstants.OP_BALOAD)); + } + + public CompactCodeAttributeComposer caload() + { + return add(new SimpleInstruction(InstructionConstants.OP_CALOAD)); + } + + public CompactCodeAttributeComposer saload() + { + return add(new SimpleInstruction(InstructionConstants.OP_SALOAD)); + } + + public CompactCodeAttributeComposer istore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex)); + } + + public CompactCodeAttributeComposer lstore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE, variableIndex)); + } + + public CompactCodeAttributeComposer fstore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE, variableIndex)); + } + + public CompactCodeAttributeComposer dstore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE, variableIndex)); + } + + public CompactCodeAttributeComposer astore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex)); + } + + public CompactCodeAttributeComposer istore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE_0)); + } + + public CompactCodeAttributeComposer istore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE_1)); + } + + public CompactCodeAttributeComposer istore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE_2)); + } + + public CompactCodeAttributeComposer istore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE_3)); + } + + public CompactCodeAttributeComposer lstore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE_0)); + } + + public CompactCodeAttributeComposer lstore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE_1)); + } + + public CompactCodeAttributeComposer lstore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE_2)); + } + + public CompactCodeAttributeComposer lstore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE_3)); + } + + public CompactCodeAttributeComposer fstore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE_0)); + } + + public CompactCodeAttributeComposer fstore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE_1)); + } + + public CompactCodeAttributeComposer fstore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE_2)); + } + + public CompactCodeAttributeComposer fstore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE_3)); + } + + public CompactCodeAttributeComposer dstore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE_0)); + } + + public CompactCodeAttributeComposer dstore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE_1)); + } + + public CompactCodeAttributeComposer dstore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE_2)); + } + + public CompactCodeAttributeComposer dstore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE_3)); + } + + public CompactCodeAttributeComposer astore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE_0)); + } + + public CompactCodeAttributeComposer astore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE_1)); + } + + public CompactCodeAttributeComposer astore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE_2)); + } + + public CompactCodeAttributeComposer astore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE_3)); + } + + public CompactCodeAttributeComposer iastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_IASTORE)); + } + + public CompactCodeAttributeComposer lastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_LASTORE)); + } + + public CompactCodeAttributeComposer fastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_FASTORE)); + } + + public CompactCodeAttributeComposer dastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_DASTORE)); + } + + public CompactCodeAttributeComposer aastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_AASTORE)); + } + + public CompactCodeAttributeComposer bastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_BASTORE)); + } + + public CompactCodeAttributeComposer castore() + { + return add(new SimpleInstruction(InstructionConstants.OP_CASTORE)); + } + + public CompactCodeAttributeComposer sastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_SASTORE)); + } + + public CompactCodeAttributeComposer pop() + { + return add(new SimpleInstruction(InstructionConstants.OP_POP)); + } + + public CompactCodeAttributeComposer pop2() + { + return add(new SimpleInstruction(InstructionConstants.OP_POP2)); + } + + public CompactCodeAttributeComposer dup() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP)); + } + + public CompactCodeAttributeComposer dup_x1() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP_X1)); + } + + public CompactCodeAttributeComposer dup_x2() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP_X2)); + } + + public CompactCodeAttributeComposer dup2() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP2)); + } + + public CompactCodeAttributeComposer dup2_x1() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP2_X1)); + } + + public CompactCodeAttributeComposer dup2_x2() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP2_X2)); + } + + public CompactCodeAttributeComposer swap() + { + return add(new SimpleInstruction(InstructionConstants.OP_SWAP)); + } + + public CompactCodeAttributeComposer iadd() + { + return add(new SimpleInstruction(InstructionConstants.OP_IADD)); + } + + public CompactCodeAttributeComposer ladd() + { + return add(new SimpleInstruction(InstructionConstants.OP_LADD)); + } + + public CompactCodeAttributeComposer fadd() + { + return add(new SimpleInstruction(InstructionConstants.OP_FADD)); + } + + public CompactCodeAttributeComposer dadd() + { + return add(new SimpleInstruction(InstructionConstants.OP_DADD)); + } + + public CompactCodeAttributeComposer isub() + { + return add(new SimpleInstruction(InstructionConstants.OP_ISUB)); + } + + public CompactCodeAttributeComposer lsub() + { + return add(new SimpleInstruction(InstructionConstants.OP_LSUB)); + } + + public CompactCodeAttributeComposer fsub() + { + return add(new SimpleInstruction(InstructionConstants.OP_FSUB)); + } + + public CompactCodeAttributeComposer dsub() + { + return add(new SimpleInstruction(InstructionConstants.OP_DSUB)); + } + + public CompactCodeAttributeComposer imul() + { + return add(new SimpleInstruction(InstructionConstants.OP_IMUL)); + } + + public CompactCodeAttributeComposer lmul() + { + return add(new SimpleInstruction(InstructionConstants.OP_LMUL)); + } + + public CompactCodeAttributeComposer fmul() + { + return add(new SimpleInstruction(InstructionConstants.OP_FMUL)); + } + + public CompactCodeAttributeComposer dmul() + { + return add(new SimpleInstruction(InstructionConstants.OP_DMUL)); + } + + public CompactCodeAttributeComposer idiv() + { + return add(new SimpleInstruction(InstructionConstants.OP_IDIV)); + } + + public CompactCodeAttributeComposer ldiv() + { + return add(new SimpleInstruction(InstructionConstants.OP_LDIV)); + } + + public CompactCodeAttributeComposer fdiv() + { + return add(new SimpleInstruction(InstructionConstants.OP_FDIV)); + } + + public CompactCodeAttributeComposer ddiv() + { + return add(new SimpleInstruction(InstructionConstants.OP_DDIV)); + } + + public CompactCodeAttributeComposer irem() + { + return add(new SimpleInstruction(InstructionConstants.OP_IREM)); + } + + public CompactCodeAttributeComposer lrem() + { + return add(new SimpleInstruction(InstructionConstants.OP_LREM)); + } + + public CompactCodeAttributeComposer frem() + { + return add(new SimpleInstruction(InstructionConstants.OP_FREM)); + } + + public CompactCodeAttributeComposer drem() + { + return add(new SimpleInstruction(InstructionConstants.OP_DREM)); + } + + public CompactCodeAttributeComposer ineg() + { + return add(new SimpleInstruction(InstructionConstants.OP_INEG)); + } + + public CompactCodeAttributeComposer lneg() + { + return add(new SimpleInstruction(InstructionConstants.OP_LNEG)); + } + + public CompactCodeAttributeComposer fneg() + { + return add(new SimpleInstruction(InstructionConstants.OP_FNEG)); + } + + public CompactCodeAttributeComposer dneg() + { + return add(new SimpleInstruction(InstructionConstants.OP_DNEG)); + } + + public CompactCodeAttributeComposer ishl() + { + return add(new SimpleInstruction(InstructionConstants.OP_ISHL)); + } + + public CompactCodeAttributeComposer lshl() + { + return add(new SimpleInstruction(InstructionConstants.OP_LSHL)); + } + + public CompactCodeAttributeComposer ishr() + { + return add(new SimpleInstruction(InstructionConstants.OP_ISHR)); + } + + public CompactCodeAttributeComposer lshr() + { + return add(new SimpleInstruction(InstructionConstants.OP_LSHR)); + } + + public CompactCodeAttributeComposer iushr() + { + return add(new SimpleInstruction(InstructionConstants.OP_IUSHR)); + } + + public CompactCodeAttributeComposer lushr() + { + return add(new SimpleInstruction(InstructionConstants.OP_LUSHR)); + } + + public CompactCodeAttributeComposer iand() + { + return add(new SimpleInstruction(InstructionConstants.OP_IAND)); + } + + public CompactCodeAttributeComposer land() + { + return add(new SimpleInstruction(InstructionConstants.OP_LAND)); + } + + public CompactCodeAttributeComposer ior() + { + return add(new SimpleInstruction(InstructionConstants.OP_IOR)); + } + + public CompactCodeAttributeComposer lor() + { + return add(new SimpleInstruction(InstructionConstants.OP_LOR)); + } + + public CompactCodeAttributeComposer ixor() + { + return add(new SimpleInstruction(InstructionConstants.OP_IXOR)); + } + + public CompactCodeAttributeComposer lxor() + { + return add(new SimpleInstruction(InstructionConstants.OP_LXOR)); + } + + public CompactCodeAttributeComposer iinc(int variableIndex, + int constant) + { + return add(new VariableInstruction(InstructionConstants.OP_IINC, variableIndex, constant)); + } + + public CompactCodeAttributeComposer i2l() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2L)); + } + + public CompactCodeAttributeComposer i2f() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2F)); + } + + public CompactCodeAttributeComposer i2d() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2D)); + } + + public CompactCodeAttributeComposer l2i() + { + return add(new SimpleInstruction(InstructionConstants.OP_L2I)); + } + + public CompactCodeAttributeComposer l2f() + { + return add(new SimpleInstruction(InstructionConstants.OP_L2F)); + } + + public CompactCodeAttributeComposer l2d() + { + return add(new SimpleInstruction(InstructionConstants.OP_L2D)); + } + + public CompactCodeAttributeComposer f2i() + { + return add(new SimpleInstruction(InstructionConstants.OP_F2I)); + } + + public CompactCodeAttributeComposer f2l() + { + return add(new SimpleInstruction(InstructionConstants.OP_F2L)); + } + + public CompactCodeAttributeComposer f2d() + { + return add(new SimpleInstruction(InstructionConstants.OP_F2D)); + } + + public CompactCodeAttributeComposer d2i() + { + return add(new SimpleInstruction(InstructionConstants.OP_D2I)); + } + + public CompactCodeAttributeComposer d2l() + { + return add(new SimpleInstruction(InstructionConstants.OP_D2L)); + } + + public CompactCodeAttributeComposer d2f() + { + return add(new SimpleInstruction(InstructionConstants.OP_D2F)); + } + + public CompactCodeAttributeComposer i2b() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2B)); + } + + public CompactCodeAttributeComposer i2c() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2C)); + } + + public CompactCodeAttributeComposer i2s() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2S)); + } + + public CompactCodeAttributeComposer lcmp() + { + return add(new SimpleInstruction(InstructionConstants.OP_LCMP)); + } + + public CompactCodeAttributeComposer fcmpl() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCMPL)); + } + + public CompactCodeAttributeComposer fcmpg() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCMPG)); + } + + public CompactCodeAttributeComposer dcmpl() + { + return add(new SimpleInstruction(InstructionConstants.OP_DCMPL)); + } + + public CompactCodeAttributeComposer dcmpg() + { + return add(new SimpleInstruction(InstructionConstants.OP_DCMPG)); + } + + public CompactCodeAttributeComposer ifeq(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFEQ, branchOffset)); + } + + public CompactCodeAttributeComposer ifne(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFNE, branchOffset)); + } + + public CompactCodeAttributeComposer iflt(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFLT, branchOffset)); + } + + public CompactCodeAttributeComposer ifge(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFGE, branchOffset)); + } + + public CompactCodeAttributeComposer ifgt(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFGT, branchOffset)); + } + + public CompactCodeAttributeComposer ifle(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFLE, branchOffset)); + } + + public CompactCodeAttributeComposer ificmpeq(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPEQ, branchOffset)); + } + + public CompactCodeAttributeComposer ificmpne(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPNE, branchOffset)); + } + + public CompactCodeAttributeComposer ificmplt(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPLT, branchOffset)); + } + + public CompactCodeAttributeComposer ificmpge(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPGE, branchOffset)); + } + + public CompactCodeAttributeComposer ificmpgt(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPGT, branchOffset)); + } + + public CompactCodeAttributeComposer ificmple(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPLE, branchOffset)); + } + + public CompactCodeAttributeComposer ifacmpeq(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFACMPEQ, branchOffset)); + } + + public CompactCodeAttributeComposer ifacmpne(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFACMPNE, branchOffset)); + } + + public CompactCodeAttributeComposer goto_(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_GOTO, branchOffset)); + } + + public CompactCodeAttributeComposer jsr(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_JSR, branchOffset)); + } + + public CompactCodeAttributeComposer ret(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_RET, variableIndex)); + } + + public CompactCodeAttributeComposer tableswitch(int defaultOffset, + int lowCase, + int highCase, + int[] jumpOffsets) + { + return add(new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH, + defaultOffset, + lowCase, + highCase, + jumpOffsets)); + } + + public CompactCodeAttributeComposer lookupswitch(int defaultOffset, + int[] cases, + int[] jumpOffsets) + { + return add(new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, + defaultOffset, + cases, + jumpOffsets)); + } + + public CompactCodeAttributeComposer ireturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_IRETURN)); + } + + public CompactCodeAttributeComposer lreturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_LRETURN)); + } + + public CompactCodeAttributeComposer freturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_FRETURN)); + } + + public CompactCodeAttributeComposer dreturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_DRETURN)); + } + + public CompactCodeAttributeComposer areturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_ARETURN)); + } + + public CompactCodeAttributeComposer return_() + { + return add(new SimpleInstruction(InstructionConstants.OP_RETURN)); + } + + public CompactCodeAttributeComposer getstatic(Clazz referencedClass, + Member referencedMember) + { + return getstatic(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public CompactCodeAttributeComposer getstatic(String className, + String name, + String descriptor) + { + return getstatic(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer getstatic(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return getstatic(constantPoolEditor.addFieldrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public CompactCodeAttributeComposer getstatic(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_GETSTATIC, constantIndex)); + } + + public CompactCodeAttributeComposer putstatic(Clazz referencedClass, + Member referencedMember) + { + return putstatic(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public CompactCodeAttributeComposer putstatic(String className, + String name, + String descriptor) + { + return putstatic(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer putstatic(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return putstatic(constantPoolEditor.addFieldrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public CompactCodeAttributeComposer putstatic(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, constantIndex)); + } + + public CompactCodeAttributeComposer getfield(String className, + String name, + String descriptor) + { + return getfield(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer getfield(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return getfield(constantPoolEditor.addFieldrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public CompactCodeAttributeComposer getfield(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_GETFIELD, constantIndex)); + } + + public CompactCodeAttributeComposer putfield(String className, + String name, + String descriptor) + { + return putfield(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer putfield(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return putfield(constantPoolEditor.addFieldrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public CompactCodeAttributeComposer putfield(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_PUTFIELD, constantIndex)); + } + + public CompactCodeAttributeComposer invokevirtual(String className, + String name, + String descriptor) + { + return invokevirtual(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer invokevirtual(Clazz referencedClass, + Member referencedMember) + { + return invokevirtual(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public CompactCodeAttributeComposer invokevirtual(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokevirtual(constantPoolEditor.addMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public CompactCodeAttributeComposer invokevirtual(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, constantIndex)); + } + + public CompactCodeAttributeComposer invokespecial(String className, + String name, + String descriptor) + { + return invokespecial(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer invokespecial(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokespecial(constantPoolEditor.addMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public CompactCodeAttributeComposer invokespecial(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, constantIndex)); + } + + public CompactCodeAttributeComposer invokestatic(String className, + String name, + String descriptor) + { + return invokestatic(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer invokestatic(Clazz referencedClass, + Member referencedMember) + { + return invokestatic(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public CompactCodeAttributeComposer invokestatic(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokestatic(constantPoolEditor.addMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public CompactCodeAttributeComposer invokestaticinterface(String className, + String name, + String descriptor) + { + return invokestaticinterface(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer invokestaticinterface(Clazz referencedClass, + Member referencedMember) + { + return invokestaticinterface(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public CompactCodeAttributeComposer invokestaticinterface(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokestatic(constantPoolEditor.addInterfaceMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public CompactCodeAttributeComposer invokestatic(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, constantIndex)); + } + + public CompactCodeAttributeComposer invokeinterface(String className, + String name, + String descriptor) + { + return invokeinterface(className, name, descriptor, null, null); + } + + public CompactCodeAttributeComposer invokeinterface(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + int invokeinterfaceConstant = + (ClassUtil.internalMethodParameterSize(descriptor, false)) << 8; + + return invokeinterface(constantPoolEditor.addInterfaceMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember), + invokeinterfaceConstant); + } + + public CompactCodeAttributeComposer invokeinterface(int constantIndex, + int constant) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKEINTERFACE, constantIndex, constant)); + } + + public CompactCodeAttributeComposer invokedynamic(int bootStrapMethodIndex, + String name, + String descriptor, + Clazz[] referencedClasses) + { + return invokedynamic(constantPoolEditor.addInvokeDynamicConstant(bootStrapMethodIndex, + name, + descriptor, + referencedClasses)); + } + + public CompactCodeAttributeComposer invokedynamic(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKEDYNAMIC, constantIndex)); + } + + public CompactCodeAttributeComposer new_(String className) + { + return new_(className, null); + } + + public CompactCodeAttributeComposer new_(String className, Clazz referencedClass) + { + return new_(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public CompactCodeAttributeComposer new_(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_NEW, constantIndex)); + } + + public CompactCodeAttributeComposer newarray(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_NEWARRAY, constant)); + } + + public CompactCodeAttributeComposer anewarray(String className, Clazz referencedClass) + { + return anewarray(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public CompactCodeAttributeComposer anewarray(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, constantIndex)); + } + + public CompactCodeAttributeComposer arraylength() + { + return add(new SimpleInstruction(InstructionConstants.OP_ARRAYLENGTH)); + } + + public CompactCodeAttributeComposer athrow() + { + return add(new SimpleInstruction(InstructionConstants.OP_ATHROW)); + } + + public CompactCodeAttributeComposer checkcast(String className) + { + return checkcast(className, null); + } + + public CompactCodeAttributeComposer checkcast(String className, Clazz referencedClass) + { + return checkcast(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public CompactCodeAttributeComposer checkcast(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_CHECKCAST, constantIndex)); + } + + public CompactCodeAttributeComposer instanceof_(String className, Clazz referencedClass) + { + return instanceof_(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public CompactCodeAttributeComposer instanceof_(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INSTANCEOF, constantIndex)); + } + + public CompactCodeAttributeComposer monitorenter() + { + return add(new SimpleInstruction(InstructionConstants.OP_MONITORENTER)); + } + + public CompactCodeAttributeComposer monitorexit() + { + return add(new SimpleInstruction(InstructionConstants.OP_MONITOREXIT)); + } + + public CompactCodeAttributeComposer wide() + { + return add(new SimpleInstruction(InstructionConstants.OP_WIDE)); + } + + public CompactCodeAttributeComposer multianewarray(String className, Clazz referencedClass) + { + return multianewarray(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public CompactCodeAttributeComposer multianewarray(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_MULTIANEWARRAY, constantIndex)); + } + + public CompactCodeAttributeComposer ifnull(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFNULL, branchOffset)); + } + + public CompactCodeAttributeComposer ifnonnull(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFNONNULL, branchOffset)); + } + + public CompactCodeAttributeComposer goto_w(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_GOTO_W, branchOffset)); + } + + public CompactCodeAttributeComposer jsr_w(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_JSR_W, branchOffset)); + } + + + // Additional convenience methods. + + /** + * Pushes the given primitive value on the stack. + * + * Operand stack: + * ... -> ..., value + * + * @param primitive the primitive value to be pushed - should never be null. + * @param internalType the internal type of the primitive ('Z','B','I',...) + */ + public CompactCodeAttributeComposer pushPrimitive(Object primitive, + char internalType) + { + switch (internalType) + { + case TYPE_BOOLEAN: return ((Boolean)primitive).booleanValue() ? iconst_1() : iconst_0(); + case TYPE_BYTE: return bipush((Byte)primitive); + case TYPE_CHAR: return ldc(((Character)primitive).charValue()); + case TYPE_SHORT: return sipush((Short)primitive); + case TYPE_INT: return ldc(((Integer)primitive).intValue()); + case TYPE_LONG: return ldc2_w((Long)primitive); + case TYPE_FLOAT: return ldc(((Float)primitive).floatValue()); + case TYPE_DOUBLE: return ldc2_w((Double)primitive); + default: throw new IllegalArgumentException(primitive.toString()); + } + } + + + /** + * Pushes the given primitive int on the stack in the most efficient way + * (as an iconst, bipush, sipush, or ldc instruction). + * + * @param value the int value to be pushed. + */ + public CompactCodeAttributeComposer pushInt(int value) + { + return + value >= -1 && + value <= 5 ? iconst(value) : + value == (byte)value ? bipush(value) : + value == (short)value ? sipush(value) : + ldc(value); + } + + + /** + * Pushes the given primitive float on the stack in the most efficient way + * (as an fconst or ldc instruction). + * + * @param value the int value to be pushed. + */ + public CompactCodeAttributeComposer pushFloat(float value) + { + return + value == 0f || + value == 1f ? fconst((int)value) : + ldc(value); + } + + + /** + * Pushes the given primitive long on the stack in the most efficient way + * (as an lconst or ldc instruction). + * + * @param value the int value to be pushed. + */ + public CompactCodeAttributeComposer pushLong(long value) + { + return + value == 0L || + value == 1L ? lconst((int)value) : + ldc2_w(value); + } + + + /** + * Pushes the given primitive double on the stack in the most efficient way + * (as a dconst or ldc instruction). + * + * @param value the int value to be pushed. + */ + public CompactCodeAttributeComposer pushDouble(double value) + { + return + value == 0. || + value == 1. ? dconst((int)value) : + ldc2_w(value); + } + + + /** + * Pushes a new array on the stack. + * + * Operand stack: + * ... -> ..., array + * + * @param elementTypeOrClassName the array element type (or class name in case of objects). + * @param size the size of the array to be created. + */ + public CompactCodeAttributeComposer pushNewArray(String elementTypeOrClassName, + int size) + { + // Create new array. + pushInt(size); + + return ClassUtil.isInternalPrimitiveType(elementTypeOrClassName) ? + newarray(InstructionUtil.arrayTypeFromInternalType(elementTypeOrClassName.charAt(0))) : + anewarray(elementTypeOrClassName, null); + } + + + /** + * Loads the given variable onto the stack. + * + * Operand stack: + * ... -> ..., value + * + * @param variableIndex the index of the variable to be loaded. + * @param internalType the type of the variable to be loaded. + */ + public CompactCodeAttributeComposer load(int variableIndex, + String internalType) + { + return load(variableIndex, internalType.charAt(0)); + } + + + /** + * Loads the given variable of primitive type onto the stack. + * + * Operand stack: + * ... -> ..., value + * + * @param variableIndex the index of the variable to be loaded. + * @param internalType the primitive type of the variable to be loaded. + */ + public CompactCodeAttributeComposer load(int variableIndex, + char internalType) + { + switch (internalType) + { + case TYPE_BOOLEAN: + case TYPE_BYTE: + case TYPE_CHAR: + case TYPE_SHORT: + case TYPE_INT: return iload(variableIndex); + case TYPE_LONG: return lload(variableIndex); + case TYPE_FLOAT: return fload(variableIndex); + case TYPE_DOUBLE: return dload(variableIndex); + default: return aload(variableIndex); + } + } + + + /** + * Stores the value on top of the stack in the variable with given index. + * + * Operand stsack: + * ..., value -> ... + * + * @param variableIndex the index of the variable where to store the + * value. + * @param internalType the type of the value to be stored. + */ + public CompactCodeAttributeComposer store(int variableIndex, + String internalType) + { + return store(variableIndex, internalType.charAt(0)); + } + + + /** + * Stores the primitve value on top of the stack in the variable with given + * index. + * + * Operand stack: + * ..., value -> ... + * + * @param variableIndex the index of the variable where to store the + * value. + * @param internalType the primitive type of the value to be stored. + */ + public CompactCodeAttributeComposer store(int variableIndex, + char internalType) + { + switch (internalType) + { + case TYPE_BOOLEAN: + case TYPE_BYTE: + case TYPE_CHAR: + case TYPE_SHORT: + case TYPE_INT: return istore(variableIndex); + case TYPE_LONG: return lstore(variableIndex); + case TYPE_FLOAT: return fstore(variableIndex); + case TYPE_DOUBLE: return dstore(variableIndex); + default: return astore(variableIndex); + } + } + + + /** + * Stores an element to an array. + * + * Operand stack: + * ..., array, index, value -> ... + * + * @param elementType the type of the value to be stored. + */ + public CompactCodeAttributeComposer storeToArray(String elementType) + { + switch (elementType.charAt(0)) + { + case TYPE_BOOLEAN: + case TYPE_BYTE: return bastore(); + case TYPE_CHAR: return castore(); + case TYPE_SHORT: return sastore(); + case TYPE_INT: return iastore(); + case TYPE_LONG: return lastore(); + case TYPE_FLOAT: return fastore(); + case TYPE_DOUBLE: return dastore(); + default: return aastore(); + } + } + + + /** + * Appends the proper return statement for the given internal type. + * + * @param internalType the return type. + */ + public CompactCodeAttributeComposer return_(String internalType) + { + switch (internalType.charAt(0)) + { + case TYPE_BOOLEAN: + case TYPE_BYTE: + case TYPE_CHAR: + case TYPE_SHORT: + case TYPE_INT: return ireturn(); + case TYPE_LONG: return lreturn(); + case TYPE_FLOAT: return freturn(); + case TYPE_DOUBLE: return dreturn(); + case TYPE_VOID: return return_(); + default: return areturn(); + } + } + + + /** + * Appends instructions to print out the given message and the top int on + * the stack. + */ + public CompactCodeAttributeComposer appendPrintIntegerInstructions(String message) + { + appendPrintInstructions(message); + appendPrintIntegerInstructions(); + return this; + } + + /** + * Appends instructions to print out the given message and the top int on + * the stack as a hexadecimal value. + */ + public CompactCodeAttributeComposer appendPrintIntegerHexInstructions(String message) + { + appendPrintInstructions(message); + appendPrintIntegerHexInstructions(); + return this; + } + + /** + * Appends instructions to print out the given message and the top long on + * the stack. + */ + public CompactCodeAttributeComposer appendPrintLongInstructions(String message) + { + appendPrintInstructions(message); + appendPrintLongInstructions(); + return this; + } + + /** + * Appends instructions to print out the given message and the top String on + * the stack. + */ + public CompactCodeAttributeComposer appendPrintStringInstructions(String message) + { + appendPrintInstructions(message); + appendPrintStringInstructions(); + return this; + } + + /** + * Appends instructions to print out the given message and the top Object on + * the stack. + */ + public CompactCodeAttributeComposer appendPrintObjectInstructions(String message) + { + appendPrintInstructions(message); + appendPrintObjectInstructions(); + return this; + } + + /** + * Appends instructions to print out the given message and the stack trace + * of the top Throwable on the stack. + */ + public CompactCodeAttributeComposer appendPrintStackTraceInstructions(String message) + { + appendPrintInstructions(message); + appendPrintStackTraceInstructions(); + return this; + } + + /** + * Appends instructions to print out the given message. + */ + public CompactCodeAttributeComposer appendPrintInstructions(String message) + { + getstatic("java/lang/System", "err", "Ljava/io/PrintStream;"); + ldc(message); + invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + return this; + } + + /** + * Appends instructions to print out the top int on the stack. + */ + public CompactCodeAttributeComposer appendPrintIntegerInstructions() + { + dup(); + getstatic("java/lang/System", "err", "Ljava/io/PrintStream;"); + swap(); + invokevirtual("java/io/PrintStream", "println", "(I)V"); + return this; + } + + /** + * Appends instructions to print out the top integer on the stack as a + * hexadecimal value. + */ + public CompactCodeAttributeComposer appendPrintIntegerHexInstructions() + { + dup(); + getstatic("java/lang/System", "err", "Ljava/io/PrintStream;"); + swap(); + invokestatic("java/lang/Integer", "toHexString", "(I)Ljava/lang/String;"); + invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + return this; + } + + /** + * Appends instructions to print out the top long on the stack. + */ + public CompactCodeAttributeComposer appendPrintLongInstructions() + { + dup2(); + getstatic("java/lang/System", "err", "Ljava/io/PrintStream;"); + dup_x2(); + pop(); + invokevirtual("java/io/PrintStream", "println", "(J)V"); + return this; + } + + /** + * Appends instructions to print out the top String on the stack. + */ + public CompactCodeAttributeComposer appendPrintStringInstructions() + { + dup(); + getstatic("java/lang/System", "err", "Ljava/io/PrintStream;"); + swap(); + invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + return this; + } + + /** + * Appends instructions to print out the top Object on the stack. + */ + public CompactCodeAttributeComposer appendPrintObjectInstructions() + { + dup(); + getstatic("java/lang/System", "err", "Ljava/io/PrintStream;"); + swap(); + invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/Object;)V"); + return this; + } + + /** + * Appends instructions to print out the stack trace of the top Throwable + * on the stack. + */ + public CompactCodeAttributeComposer appendPrintStackTraceInstructions() + { + dup(); + invokevirtual("java/lang/Throwable", "printStackTrace", "()V"); + return this; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); + } + + + // Small utility methods. + + /** + * Adds the given instruction, shrinking it if necessary. + */ + private CompactCodeAttributeComposer add(Instruction instruction) + { + codeAttributeComposer.appendInstruction(instruction); + + return this; + } + + + public static void main(String[] args) + { + ProgramClass targetClass = new ProgramClass(0, 0, new Constant[32], 0, 0, 0); + + CompactCodeAttributeComposer composer = new CompactCodeAttributeComposer(targetClass); + + 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/ComparableConstant.java b/core/src/proguard/classfile/editor/ComparableConstant.java similarity index 78% rename from src/proguard/classfile/editor/ComparableConstant.java rename to core/src/proguard/classfile/editor/ComparableConstant.java index 55d3c2767..533127824 100644 --- a/src/proguard/classfile/editor/ComparableConstant.java +++ b/core/src/proguard/classfile/editor/ComparableConstant.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,6 +24,7 @@ import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.util.SimplifiedVisitor; +import proguard.util.ArrayUtil; /** @@ -39,7 +40,7 @@ class ComparableConstant extends SimplifiedVisitor implements Comparable, ConstantVisitor { - private static final int[] PRIORITIES = new int[19]; + private static final int[] PRIORITIES = new int[22]; static { PRIORITIES[ClassConstants.CONSTANT_Integer] = 0; // Possibly byte index (ldc). @@ -55,7 +56,10 @@ class ComparableConstant PRIORITIES[ClassConstants.CONSTANT_MethodHandle] = 10; PRIORITIES[ClassConstants.CONSTANT_NameAndType] = 11; PRIORITIES[ClassConstants.CONSTANT_MethodType] = 12; - PRIORITIES[ClassConstants.CONSTANT_Utf8] = 13; + PRIORITIES[ClassConstants.CONSTANT_Module] = 13; + PRIORITIES[ClassConstants.CONSTANT_Package] = 14; + PRIORITIES[ClassConstants.CONSTANT_Utf8] = 15; + PRIORITIES[ClassConstants.CONSTANT_PrimitiveArray] = 16; } private final Clazz clazz; @@ -153,6 +157,35 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) ((DoubleConstant)otherConstant).getValue()); } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + PrimitiveArrayConstant otherPrimitiveArrayConstant = + (PrimitiveArrayConstant)otherConstant; + + char primitiveType = primitiveArrayConstant.getPrimitiveType(); + char otherPrimitiveType = otherPrimitiveArrayConstant.getPrimitiveType(); + + if (primitiveType != otherPrimitiveType) + { + result = primitiveType < otherPrimitiveType ? -1 : 1; + } + else + { + Object values = primitiveArrayConstant.getValues(); + Object otherValues = otherPrimitiveArrayConstant.getValues(); + + result = + values instanceof boolean[] ? ArrayUtil.compare((boolean[])values, ((boolean[])values).length, (boolean[])otherValues, ((boolean[])otherValues).length) : + values instanceof byte[] ? ArrayUtil.compare((byte[]) values, ((byte[]) values).length, (byte[]) otherValues, ((byte[]) otherValues).length) : + values instanceof char[] ? ArrayUtil.compare((char[]) values, ((char[]) values).length, (char[]) otherValues, ((char[]) otherValues).length) : + values instanceof short[] ? ArrayUtil.compare((short[]) values, ((short[]) values).length, (short[]) otherValues, ((short[]) otherValues).length) : + values instanceof int[] ? ArrayUtil.compare((int[]) values, ((int[]) values).length, (int[]) otherValues, ((int[]) otherValues).length) : + values instanceof float[] ? ArrayUtil.compare((float[]) values, ((float[]) values).length, (float[]) otherValues, ((float[]) otherValues).length) : + values instanceof long[] ? ArrayUtil.compare((long[]) values, ((long[]) values).length, (long[]) otherValues, ((long[]) otherValues).length) : + /*values instanceof double[] */ ArrayUtil.compare((double[]) values, ((double[]) values).length, (double[]) otherValues, ((double[]) otherValues).length); + } + } + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { result = stringConstant.getString(clazz).compareTo(((StringConstant)otherConstant).getString(clazz)); @@ -229,6 +262,17 @@ public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTyp } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + result = moduleConstant.getName(clazz).compareTo(((ModuleConstant)otherConstant).getName(clazz)); + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + result = packageConstant.getName(clazz).compareTo(((PackageConstant)otherConstant).getName(clazz)); + } + // Implementations for Object. public boolean equals(Object other) diff --git a/src/proguard/classfile/editor/ConstantAdder.java b/core/src/proguard/classfile/editor/ConstantAdder.java similarity index 93% rename from src/proguard/classfile/editor/ConstantAdder.java rename to core/src/proguard/classfile/editor/ConstantAdder.java index ed1fd934c..3d15b71aa 100644 --- a/src/proguard/classfile/editor/ConstantAdder.java +++ b/core/src/proguard/classfile/editor/ConstantAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -118,6 +118,13 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + constantIndex = + constantPoolEditor.addPrimitiveArrayConstant(primitiveArrayConstant.getValues()); + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { constantIndex = @@ -182,6 +189,20 @@ public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHa } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + constantIndex = + constantPoolEditor.addModuleConstant(moduleConstant.getName(clazz)); + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + constantIndex = + constantPoolEditor.addPackageConstant(packageConstant.getName(clazz)); + } + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { // First add the referenced class constant, with its own referenced class. diff --git a/src/proguard/classfile/editor/ConstantPoolEditor.java b/core/src/proguard/classfile/editor/ConstantPoolEditor.java similarity index 82% rename from src/proguard/classfile/editor/ConstantPoolEditor.java rename to core/src/proguard/classfile/editor/ConstantPoolEditor.java index 1f6e950de..bc7bd0e6d 100644 --- a/src/proguard/classfile/editor/ConstantPoolEditor.java +++ b/core/src/proguard/classfile/editor/ConstantPoolEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,6 +22,9 @@ import proguard.classfile.*; import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.ClassReferenceInitializer; +import proguard.optimize.peephole.WildcardConstantFilter; /** * This class can add constant pool entries to a given class. @@ -32,16 +35,48 @@ public class ConstantPoolEditor { private static final boolean DEBUG = false; - private ProgramClass targetClass; + private final ProgramClass targetClass; + private final ConstantVisitor constantReferenceInitializer; /** - * Creates a new ConstantPoolEditor that will edit constants in the given - * target class. + * Creates a new ConstantPoolEditor. + * @param targetClass the target class in which constants are to be edited. */ public ConstantPoolEditor(ProgramClass targetClass) + { + this(targetClass, null, null); + } + + + /** + * Creates a new ConstantPoolEditor that automatically initializes class + * references and class member references in new constants. + * @param targetClass the target class in which constants are to be + * edited. + * @param programClassPool the program class pool from which new constants + * can be initialized. + * @param libraryClassPool the library class pool from which new constants + * can be initialized. + */ + public ConstantPoolEditor(ProgramClass targetClass, + ClassPool programClassPool, + ClassPool libraryClassPool) { this.targetClass = targetClass; + + constantReferenceInitializer = programClassPool == null ? null : + new WildcardConstantFilter( + new ClassReferenceInitializer(programClassPool, libraryClassPool)); + } + + + /** + * Returns the target class in which constants are edited. + */ + public ProgramClass getTargetClass() + { + return targetClass; } @@ -164,6 +199,17 @@ public int addDoubleConstant(double value) } + /** + * Finds or creates a PrimitiveArrayConstant constant pool entry with the + * given values. + * @return the constant pool index of the PrimitiveArrayConstant. + */ + public int addPrimitiveArrayConstant(Object values) + { + return addConstant(new PrimitiveArrayConstant(values)); + } + + /** * Finds or creates a StringConstant constant pool entry with the given * value. @@ -185,7 +231,10 @@ public int addStringConstant(String string, constant.getTag() == ClassConstants.CONSTANT_String) { StringConstant stringConstant = (StringConstant)constant; - if (stringConstant.getString(targetClass).equals(string)) + if (stringConstant.u2stringIndex < constantPoolCount && + stringConstant.getString(targetClass).equals(string) && + stringConstant.referencedClass == referencedClass && + stringConstant.referencedMember == referencedMember) { return index; } @@ -238,7 +287,7 @@ public int addInvokeDynamicConstant(int bootstrapMethodIndex, { InvokeDynamicConstant invokeDynamicConstant = (InvokeDynamicConstant)constant; if (invokeDynamicConstant.u2bootstrapMethodAttributeIndex == bootstrapMethodIndex && - invokeDynamicConstant.u2nameAndTypeIndex == nameAndTypeIndex) + invokeDynamicConstant.u2nameAndTypeIndex == nameAndTypeIndex) { return index; } @@ -284,6 +333,66 @@ public int addMethodHandleConstant(int referenceKind, referenceIndex)); } + /** + * Finds or creates a ModuleConstant constant pool entry with the given name. + * @return the constant pool index of the ModuleConstant. + */ + public int addModuleConstant(String name) + { + 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_Module) + { + ModuleConstant moduleConstant = (ModuleConstant)constant; + if (moduleConstant.getName(targetClass).equals(name)) + { + return index; + } + } + } + + int nameIndex = addUtf8Constant(name); + + return addConstant(new ModuleConstant(nameIndex)); + } + + /** + * Finds or creates a PackageConstant constant pool entry with the given name. + * @return the constant pool index of the PackageConstant. + */ + public int addPackageConstant(String name) + { + 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_Package) + { + PackageConstant packageConstant = (PackageConstant)constant; + if (packageConstant.getName(targetClass).equals(name)) + { + return index; + } + } + } + + int nameIndex = addUtf8Constant(name); + + return addConstant(new PackageConstant(nameIndex)); + } + /** * Finds or creates a FieldrefConstant constant pool entry for the given @@ -377,8 +486,8 @@ public int addFieldrefConstant(int classIndex, constant.getTag() == ClassConstants.CONSTANT_Fieldref) { FieldrefConstant fieldrefConstant = (FieldrefConstant)constant; - if (fieldrefConstant.u2classIndex == classIndex && - fieldrefConstant.u2nameAndTypeIndex == nameAndTypeIndex) + if (fieldrefConstant.u2classIndex == classIndex && + fieldrefConstant.u2nameAndTypeIndex == nameAndTypeIndex) { return index; } @@ -481,7 +590,7 @@ public int addInterfaceMethodrefConstant(int classIndex, Constant constant = constantPool[index]; if (constant != null && - constant.getTag() == ClassConstants.CONSTANT_InterfaceMethodref) + constant.getTag() == ClassConstants.CONSTANT_InterfaceMethodref) { InterfaceMethodrefConstant methodrefConstant = (InterfaceMethodrefConstant)constant; if (methodrefConstant.u2classIndex == classIndex && @@ -636,7 +745,8 @@ public int addClassConstant(String name, constant.getTag() == ClassConstants.CONSTANT_Class) { ClassConstant classConstant = (ClassConstant)constant; - if (classConstant.getName(targetClass).equals(name)) + if (classConstant.u2nameIndex < constantPoolCount && + classConstant.getName(targetClass).equals(name)) { return index; } @@ -669,7 +779,8 @@ public int addMethodTypeConstant(String type, constant.getTag() == ClassConstants.CONSTANT_MethodType) { MethodTypeConstant methodTypeConstant = (MethodTypeConstant)constant; - if (methodTypeConstant.getType(targetClass).equals(type)) + if (methodTypeConstant.u2descriptorIndex < constantPoolCount && + methodTypeConstant.getType(targetClass).equals(type)) { return index; } @@ -701,7 +812,9 @@ public int addNameAndTypeConstant(String name, constant.getTag() == ClassConstants.CONSTANT_NameAndType) { NameAndTypeConstant nameAndTypeConstant = (NameAndTypeConstant)constant; - if (nameAndTypeConstant.getName(targetClass).equals(name) && + if (nameAndTypeConstant.u2nameIndex < constantPoolCount && + nameAndTypeConstant.u2descriptorIndex < constantPoolCount && + nameAndTypeConstant.getName(targetClass).equals(name) && nameAndTypeConstant.getType(targetClass).equals(type)) { return index; @@ -744,7 +857,7 @@ public int addUtf8Constant(String string) /** - * Adds a given constant pool entry to the end of the constant pool/ + * 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) @@ -755,19 +868,23 @@ public int addConstant(Constant constant) // Make sure there is enough space for another constant pool entry. if (constantPool.length < constantPoolCount+2) { - targetClass.constantPool = new Constant[constantPoolCount+2]; + Constant[] newConstantPool = new Constant[constantPoolCount+2]; System.arraycopy(constantPool, 0, - targetClass.constantPool, 0, + newConstantPool, 0, constantPoolCount); - constantPool = targetClass.constantPool; + + // Assign the newly created constant pool after all entries + // have been copied to avoid race-conditions. + targetClass.constantPool = newConstantPool; + constantPool = targetClass.constantPool; } if (DEBUG) { - System.out.println(targetClass.getName()+": adding ["+constant.getClass().getName()+"] at index "+targetClass.u2constantPoolCount); + System.out.println("ConstantPoolEditor: ["+(targetClass.u2thisClass > 0 ? targetClass.getName() : "(dummy)")+"] adding ["+constant.getClass().getName()+"] at index "+targetClass.u2constantPoolCount); } - // Create a new Utf8Constant for the given string. + // Add the new entry to the end of the constant pool. constantPool[targetClass.u2constantPoolCount++] = constant; // Long constants and double constants take up two entries in the @@ -779,6 +896,13 @@ public int addConstant(Constant constant) constantPool[targetClass.u2constantPoolCount++] = null; } + // Initialize the class references and class member references in the + // constant, if necessary. + if (constantReferenceInitializer != null) + { + constant.accept(targetClass, constantReferenceInitializer); + } + return constantPoolCount; } } diff --git a/src/proguard/classfile/editor/ConstantPoolRemapper.java b/core/src/proguard/classfile/editor/ConstantPoolRemapper.java similarity index 86% rename from src/proguard/classfile/editor/ConstantPoolRemapper.java rename to core/src/proguard/classfile/editor/ConstantPoolRemapper.java index 562854d4c..f794fdaa0 100644 --- a/src/proguard/classfile/editor/ConstantPoolRemapper.java +++ b/core/src/proguard/classfile/editor/ConstantPoolRemapper.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,6 +24,8 @@ import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.module.*; +import proguard.classfile.attribute.module.visitor.*; import proguard.classfile.attribute.preverification.*; import proguard.classfile.attribute.preverification.visitor.*; import proguard.classfile.attribute.visitor.*; @@ -34,6 +36,8 @@ import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.*; +import java.util.Arrays; + /** * 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 @@ -56,6 +60,10 @@ public class ConstantPoolRemapper ParameterInfoVisitor, LocalVariableInfoVisitor, LocalVariableTypeInfoVisitor, + RequiresInfoVisitor, + ExportsInfoVisitor, + OpensInfoVisitor, + ProvidesInfoVisitor, AnnotationVisitor, ElementValueVisitor { @@ -85,7 +93,7 @@ public void visitProgramClass(ProgramClass programClass) remapConstantIndexArray(programClass.u2interfaces, programClass.u2interfacesCount); - // Remap the references of the contant pool entries themselves. + // Remap the references of the constant pool entries themselves. programClass.constantPoolEntriesAccept(this); // Remap the references in all fields, methods, and attributes. @@ -126,6 +134,12 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + // Nothing to do. + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { stringConstant.u2stringIndex = @@ -152,6 +166,18 @@ public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHa remapConstantIndex(methodHandleConstant.u2referenceIndex); } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + moduleConstant.u2nameIndex = + remapConstantIndex(moduleConstant.u2nameIndex); + } + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + packageConstant.u2nameIndex = + remapConstantIndex(packageConstant.u2nameIndex); + } + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { @@ -302,6 +328,43 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + moduleAttribute.u2attributeNameIndex = + remapConstantIndex(moduleAttribute.u2attributeNameIndex); + moduleAttribute.u2moduleNameIndex = + remapConstantIndex(moduleAttribute.u2moduleNameIndex); + + if (moduleAttribute.u2moduleVersionIndex != 0) + { + moduleAttribute.u2moduleVersionIndex = + remapConstantIndex(moduleAttribute.u2moduleVersionIndex); + } + moduleAttribute.requiresAccept(clazz, this); + moduleAttribute.exportsAccept(clazz, this); + moduleAttribute.opensAccept(clazz, this); + remapConstantIndexArray(moduleAttribute.u2uses, moduleAttribute.u2usesCount); + moduleAttribute.providesAccept(clazz, this); + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + moduleMainClassAttribute.u2attributeNameIndex = + remapConstantIndex(moduleMainClassAttribute.u2attributeNameIndex); + moduleMainClassAttribute.u2mainClass = + remapConstantIndex(moduleMainClassAttribute.u2mainClass); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + modulePackagesAttribute.u2attributeNameIndex = + remapConstantIndex(modulePackagesAttribute.u2attributeNameIndex); + remapConstantIndexArray(modulePackagesAttribute.u2packages, modulePackagesAttribute.u2packagesCount); + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { deprecatedAttribute.u2attributeNameIndex = @@ -594,6 +657,46 @@ public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute } + // Implementations for RequiresInfoVisitor. + + public void visitRequiresInfo(Clazz clazz, RequiresInfo requiresInfo) + { + requiresInfo.u2requiresIndex = + remapConstantIndex(requiresInfo.u2requiresIndex); + requiresInfo.u2requiresVersionIndex = + remapConstantIndex(requiresInfo.u2requiresVersionIndex); + } + + + // Implementations for ExportsInfoVisitor. + + public void visitExportsInfo(Clazz clazz, ExportsInfo exportsInfo) + { + exportsInfo.u2exportsIndex = + remapConstantIndex(exportsInfo.u2exportsIndex); + remapConstantIndexArray(exportsInfo.u2exportsToIndex, exportsInfo.u2exportsToCount); + } + + + // Implementations for OpensInfoVisitor. + + public void visitOpensInfo(Clazz clazz, OpensInfo opensInfo) + { + opensInfo.u2opensIndex = + remapConstantIndex(opensInfo.u2opensIndex); + remapConstantIndexArray(opensInfo.u2opensToIndex, opensInfo.u2opensToCount); + } + + + // Implementations for ProvidesInfoVisitor. + + public void visitProvidesInfo(Clazz clazz, ProvidesInfo providesInfo) + { + providesInfo.u2providesIndex = + remapConstantIndex(providesInfo.u2providesIndex); + remapConstantIndexArray(providesInfo.u2providesWithIndex, providesInfo.u2providesWithCount); + } + // Implementations for AnnotationVisitor. public void visitAnnotation(Clazz clazz, Annotation annotation) diff --git a/src/proguard/classfile/editor/ConstantPoolShrinker.java b/core/src/proguard/classfile/editor/ConstantPoolShrinker.java similarity index 84% rename from src/proguard/classfile/editor/ConstantPoolShrinker.java rename to core/src/proguard/classfile/editor/ConstantPoolShrinker.java index 08231648b..9bad8e7f1 100644 --- a/src/proguard/classfile/editor/ConstantPoolShrinker.java +++ b/core/src/proguard/classfile/editor/ConstantPoolShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,6 +24,8 @@ import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.module.*; +import proguard.classfile.attribute.module.visitor.*; import proguard.classfile.attribute.preverification.*; import proguard.classfile.attribute.preverification.visitor.*; import proguard.classfile.attribute.visitor.*; @@ -44,6 +46,8 @@ public class ConstantPoolShrinker extends SimplifiedVisitor implements ClassVisitor, + + // Implementation interfaces. MemberVisitor, ConstantVisitor, AttributeVisitor, @@ -55,12 +59,17 @@ public class ConstantPoolShrinker ParameterInfoVisitor, LocalVariableInfoVisitor, LocalVariableTypeInfoVisitor, + RequiresInfoVisitor, + ExportsInfoVisitor, + OpensInfoVisitor, + ProvidesInfoVisitor, AnnotationVisitor, ElementValueVisitor, InstructionVisitor { - // A visitor info flag to indicate the constant is being used. - private static final Object USED = new Object(); + // A visitor info flag to indicate that the constant is being used. + // Don't make a static instance, so we don't need to clear any old flags. + private final Object USED = new Object(); private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); @@ -186,6 +195,22 @@ public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTyp } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + markAsUsed(moduleConstant); + + markConstant(clazz, moduleConstant.u2nameIndex); + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + markAsUsed(packageConstant); + + markConstant(clazz, packageConstant.u2nameIndex); + } + + // Implementations for AttributeVisitor. public void visitAnyAttribute(Clazz clazz, Attribute attribute) @@ -238,6 +263,46 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + markConstant(clazz, moduleAttribute.u2attributeNameIndex); + markConstant(clazz, moduleAttribute.u2moduleNameIndex); + + if (moduleAttribute.u2moduleVersionIndex != 0) + { + markConstant(clazz, moduleAttribute.u2moduleVersionIndex); + } + moduleAttribute.requiresAccept(clazz, this); + moduleAttribute.exportsAccept(clazz, this); + moduleAttribute.opensAccept(clazz, this); + + for (int index = 0; index < moduleAttribute.u2usesCount; index++) + { + markConstant(clazz, moduleAttribute.u2uses[index]); + } + + moduleAttribute.providesAccept(clazz, this); + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + markConstant(clazz, moduleMainClassAttribute.u2attributeNameIndex); + markConstant(clazz, moduleMainClassAttribute.u2mainClass); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + markConstant(clazz, modulePackagesAttribute.u2attributeNameIndex); + + for (int index = 0; index < modulePackagesAttribute.u2packagesCount; index++) + { + markConstant(clazz, modulePackagesAttribute.u2packages[index]); + } + } + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) { markConstant(clazz, signatureAttribute.u2attributeNameIndex); @@ -419,7 +484,10 @@ public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttrib public void visitParameterInfo(Clazz clazz, Method method, int parameterIndex, ParameterInfo parameterInfo) { - markConstant(clazz, parameterInfo.u2nameIndex); + if (parameterInfo.u2nameIndex != 0) + { + markConstant(clazz, parameterInfo.u2nameIndex); + } } @@ -441,6 +509,54 @@ public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute } + // Implementations for RequiresInfoVisitor. + + public void visitRequiresInfo(Clazz clazz, RequiresInfo requiresInfo) + { + markConstant(clazz, requiresInfo.u2requiresIndex); + markConstant(clazz, requiresInfo.u2requiresVersionIndex); + } + + + // Implementations for ExportsInfoVisitor. + + public void visitExportsInfo(Clazz clazz, ExportsInfo exportsInfo) + { + markConstant(clazz, exportsInfo.u2exportsIndex); + + for (int index = 0; index < exportsInfo.u2exportsToCount; index++) + { + markConstant(clazz, exportsInfo.u2exportsToIndex[index]); + } + } + + + // Implementations for OpensInfoVisitor. + + public void visitOpensInfo(Clazz clazz, OpensInfo opensInfo) + { + markConstant(clazz, opensInfo.u2opensIndex); + + for (int index = 0; index < opensInfo.u2opensToCount; index++) + { + markConstant(clazz, opensInfo.u2opensToIndex[index]); + } + } + + + // Implementations for ProvidesInfoVisitor. + + public void visitProvidesInfo(Clazz clazz, ProvidesInfo providesInfo) + { + markConstant(clazz, providesInfo.u2providesIndex); + + for (int index = 0; index < providesInfo.u2providesWithCount; index++) + { + markConstant(clazz, providesInfo.u2providesWithIndex[index]); + } + } + + // Implementations for AnnotationVisitor. public void visitAnnotation(Clazz clazz, Annotation annotation) diff --git a/src/proguard/classfile/editor/ConstantPoolSorter.java b/core/src/proguard/classfile/editor/ConstantPoolSorter.java similarity index 98% rename from src/proguard/classfile/editor/ConstantPoolSorter.java rename to core/src/proguard/classfile/editor/ConstantPoolSorter.java index 9fc2e3307..ea2f4e19e 100644 --- a/src/proguard/classfile/editor/ConstantPoolSorter.java +++ b/core/src/proguard/classfile/editor/ConstantPoolSorter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ElementValueAdder.java b/core/src/proguard/classfile/editor/ElementValueAdder.java similarity index 99% rename from src/proguard/classfile/editor/ElementValueAdder.java rename to core/src/proguard/classfile/editor/ElementValueAdder.java index 808adbdfe..54d2a8490 100644 --- a/src/proguard/classfile/editor/ElementValueAdder.java +++ b/core/src/proguard/classfile/editor/ElementValueAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ElementValuesEditor.java b/core/src/proguard/classfile/editor/ElementValuesEditor.java similarity index 99% rename from src/proguard/classfile/editor/ElementValuesEditor.java rename to core/src/proguard/classfile/editor/ElementValuesEditor.java index 4a4d0f7c0..69c1d89fe 100644 --- a/src/proguard/classfile/editor/ElementValuesEditor.java +++ b/core/src/proguard/classfile/editor/ElementValuesEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ExceptionAdder.java b/core/src/proguard/classfile/editor/ExceptionAdder.java similarity index 97% rename from src/proguard/classfile/editor/ExceptionAdder.java rename to core/src/proguard/classfile/editor/ExceptionAdder.java index 74b6ca2d9..af08c68ce 100644 --- a/src/proguard/classfile/editor/ExceptionAdder.java +++ b/core/src/proguard/classfile/editor/ExceptionAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ExceptionInfoAdder.java b/core/src/proguard/classfile/editor/ExceptionInfoAdder.java similarity index 97% rename from src/proguard/classfile/editor/ExceptionInfoAdder.java rename to core/src/proguard/classfile/editor/ExceptionInfoAdder.java index 2784bd3d0..e51efe487 100644 --- a/src/proguard/classfile/editor/ExceptionInfoAdder.java +++ b/core/src/proguard/classfile/editor/ExceptionInfoAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/editor/ExceptionInfoEditor.java b/core/src/proguard/classfile/editor/ExceptionInfoEditor.java new file mode 100644 index 000000000..8fd1a6351 --- /dev/null +++ b/core/src/proguard/classfile/editor/ExceptionInfoEditor.java @@ -0,0 +1,92 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.*; +import proguard.util.ArrayUtil; + +/** + * This class can add exceptions to the exception table of a given code + * attribute. The exceptions must have been filled out beforehand. + * + * @author Eric Lafortune + */ +public class ExceptionInfoEditor +{ + private final CodeAttribute codeAttribute; + + + /** + * Creates a new ExceptionInfoEditor that can add exceptions to the + * given code attribute. + */ + public ExceptionInfoEditor(CodeAttribute codeAttribute) + { + this.codeAttribute = codeAttribute; + } + + + /** + * Prepends the given exception to the exception table. + */ + void prependException(ExceptionInfo exceptionInfo) + { + ExceptionInfo[] exceptionTable = codeAttribute.exceptionTable; + int exceptionTableLength = codeAttribute.u2exceptionTableLength; + + int newExceptionTableLength = exceptionTableLength + 1; + + // Is the exception table large enough? + if (exceptionTable.length < newExceptionTableLength) + { + ExceptionInfo[] newExceptionTable = + new ExceptionInfo[newExceptionTableLength]; + + System.arraycopy(exceptionTable, 0, + newExceptionTable, 1, + exceptionTableLength); + newExceptionTable[0] = exceptionInfo; + + codeAttribute.exceptionTable = newExceptionTable; + } + else + { + System.arraycopy(exceptionTable, 0, + exceptionTable, 1, + exceptionTableLength); + exceptionTable[0] = exceptionInfo; + } + + codeAttribute.u2exceptionTableLength = newExceptionTableLength; + } + + + /** + * Appends the given exception to the exception table. + */ + void appendException(ExceptionInfo exceptionInfo) + { + codeAttribute.exceptionTable = + ArrayUtil.add(codeAttribute.exceptionTable, + codeAttribute.u2exceptionTableLength++, + exceptionInfo); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/ExceptionsAttributeEditor.java b/core/src/proguard/classfile/editor/ExceptionsAttributeEditor.java similarity index 97% rename from src/proguard/classfile/editor/ExceptionsAttributeEditor.java rename to core/src/proguard/classfile/editor/ExceptionsAttributeEditor.java index 783f714ca..5c3a49eac 100644 --- a/src/proguard/classfile/editor/ExceptionsAttributeEditor.java +++ b/core/src/proguard/classfile/editor/ExceptionsAttributeEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/InnerClassesAccessFixer.java b/core/src/proguard/classfile/editor/InnerClassesAccessFixer.java similarity index 97% rename from src/proguard/classfile/editor/InnerClassesAccessFixer.java rename to core/src/proguard/classfile/editor/InnerClassesAccessFixer.java index 29364cb18..e520bc714 100644 --- a/src/proguard/classfile/editor/InnerClassesAccessFixer.java +++ b/core/src/proguard/classfile/editor/InnerClassesAccessFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/editor/InnerClassesAttributeEditor.java b/core/src/proguard/classfile/editor/InnerClassesAttributeEditor.java new file mode 100644 index 000000000..e26d3ac3c --- /dev/null +++ b/core/src/proguard/classfile/editor/InnerClassesAttributeEditor.java @@ -0,0 +1,92 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.*; +import proguard.util.ArrayUtil; + +/** + * This class can add/remove bootstrap methods to/from a given inner classes + * attribute. Inner classes to be added must have been filled out beforehand. + * + * @author Thomas Neidhart + */ +public class InnerClassesAttributeEditor +{ + private InnerClassesAttribute targetInnerClassesAttribute; + + + /** + * Creates a new InnerClassesAttributeEditor that will edit inner + * classes in the given inner classes attribute. + */ + public InnerClassesAttributeEditor(InnerClassesAttribute targetInnerClassesAttribute) + { + this.targetInnerClassesAttribute = targetInnerClassesAttribute; + } + + + /** + * Adds a given inner class to the inner classes attribute. + * @return the index of the inner class. + */ + public int addInnerClassesInfo(InnerClassesInfo innerClassesInfo) + { + targetInnerClassesAttribute.classes = + ArrayUtil.add(targetInnerClassesAttribute.classes, + targetInnerClassesAttribute.u2classesCount, + innerClassesInfo); + + return targetInnerClassesAttribute.u2classesCount++; + } + + + /** + * Removes the given inner class from the inner classes attribute. + */ + public void removeInnerClassesInfo(InnerClassesInfo innerClassesInfo) + { + ArrayUtil.remove(targetInnerClassesAttribute.classes, + targetInnerClassesAttribute.u2classesCount--, + findInnerClassesInfoIndex(innerClassesInfo)); + } + + + /** + * Finds the index of the given bootstrap method info in the target attribute. + */ + private int findInnerClassesInfoIndex(InnerClassesInfo innerClassesInfo) + { + int innerClassesCount = targetInnerClassesAttribute.u2classesCount; + InnerClassesInfo[] innerClassesInfos = targetInnerClassesAttribute.classes; + + for (int index = 0; index < innerClassesCount; index++) + { + if (innerClassesInfos[index].equals(innerClassesInfo)) + { + return index; + } + } + + return innerClassesCount; + } + +} \ No newline at end of file diff --git a/src/proguard/classfile/editor/InstructionAdder.java b/core/src/proguard/classfile/editor/InstructionAdder.java similarity index 97% rename from src/proguard/classfile/editor/InstructionAdder.java rename to core/src/proguard/classfile/editor/InstructionAdder.java index 5dae480b7..e059379d8 100644 --- a/src/proguard/classfile/editor/InstructionAdder.java +++ b/core/src/proguard/classfile/editor/InstructionAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/editor/InstructionSequenceBuilder.java b/core/src/proguard/classfile/editor/InstructionSequenceBuilder.java new file mode 100644 index 000000000..17be6a0be --- /dev/null +++ b/core/src/proguard/classfile/editor/InstructionSequenceBuilder.java @@ -0,0 +1,1981 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.instruction.*; +import proguard.classfile.util.ClassUtil; +import proguard.optimize.peephole.InstructionSequenceReplacer; + +import java.util.*; + +/** + * This utility class allows to construct sequences of instructions and + * their constants. + * + * @author Eric Lafortune + */ +public class InstructionSequenceBuilder +{ + private final ConstantPoolEditor constantPoolEditor; + + private final List instructions = new ArrayList(256); + + + /** + * Creates a new InstructionSequenceBuilder. + */ + public InstructionSequenceBuilder() + { + this(null, null); + } + + + /** + * Creates a new InstructionSequenceBuilder that automatically initializes + * class references and class member references in new constants. + * @param programClassPool the program class pool from which new + * constants can be initialized. + * @param libraryClassPool the library class pool from which new + * constants can be initialized. + */ + public InstructionSequenceBuilder(ClassPool programClassPool, + ClassPool libraryClassPool) + { + this(new MyDummyClass(), + programClassPool, + libraryClassPool); + } + + + /** + * Creates a new InstructionSequenceBuilder. + * @param targetClass the target class for the instruction + * constants. + */ + public InstructionSequenceBuilder(ProgramClass targetClass) + { + this(new ConstantPoolEditor(targetClass)); + } + + + /** + * Creates a new InstructionSequenceBuilder that automatically initializes + * class references and class member references in new constants. + * @param targetClass the target class for the instruction + * constants. + * @param programClassPool the program class pool from which new + * constants can be initialized. + * @param libraryClassPool the library class pool from which new + * constants can be initialized. + */ + public InstructionSequenceBuilder(ProgramClass targetClass, + ClassPool programClassPool, + ClassPool libraryClassPool) + { + this(new ConstantPoolEditor(targetClass, programClassPool, libraryClassPool)); + } + + + /** + * Creates a new InstructionSequenceBuilder. + * @param constantPoolEditor the editor to use for creating any constants + * for the instructions. + */ + public InstructionSequenceBuilder(ConstantPoolEditor constantPoolEditor) + { + this.constantPoolEditor = constantPoolEditor; + } + + + /** + * Returns the ConstantPoolEditor used by this builder to + * create constants. + * + * @return the ConstantPoolEditor used by this builder to + * create constants. + */ + public ConstantPoolEditor getConstantPoolEditor() + { + return constantPoolEditor; + } + + + /** + * Short for {@link #appendInstruction(Instruction)}. + * + * @see InstructionSequenceReplacer#label() + */ + public InstructionSequenceBuilder label(Instruction instruction) + { + return appendInstruction(instruction); + } + + + /** + * Short for {@link #appendInstruction(Instruction)}. + * + * @see InstructionSequenceReplacer#catch_(int,int,int) + */ + public InstructionSequenceBuilder catch_(Instruction instruction) + { + return appendInstruction(instruction); + } + + + /** + * Appends the given instruction. + * @param instruction the instruction to be appended. + * @return the builder itself. + */ + public InstructionSequenceBuilder appendInstruction(Instruction instruction) + { + return add(instruction); + } + + + /** + * Appends the given instructions. + * @param instructions the instructions to be appended. + * @return the builder itself. + */ + public InstructionSequenceBuilder appendInstructions(Instruction[] instructions) + { + for (Instruction instruction : instructions) + { + add(instruction); + } + + return this; + } + + + /** + * Short for {@link #instructions()}. + */ + public Instruction[] __() + { + return instructions(); + } + + + /** + * Returns the accumulated sequence of instructions + * and resets the sequence in the builder. + */ + public Instruction[] instructions() + { + Instruction[] instructions_array = new Instruction[instructions.size()]; + instructions.toArray(instructions_array); + + instructions.clear(); + + return instructions_array; + } + + + /** + * Returns the accumulated set of constants + * and resets the set in the builder. + */ + public Constant[] constants() + { + ProgramClass targetClass = constantPoolEditor.getTargetClass(); + + Constant[] constantPool = new Constant[targetClass.u2constantPoolCount]; + System.arraycopy(targetClass.constantPool, + 0, + constantPool, + 0, + targetClass.u2constantPoolCount); + + targetClass.u2constantPoolCount = 0; + + return constantPool; + } + + + // Methods corresponding to the bytecode opcodes. + + public InstructionSequenceBuilder nop() + { + return add(new SimpleInstruction(InstructionConstants.OP_NOP)); + } + + public InstructionSequenceBuilder aconst_null() + { + return add(new SimpleInstruction(InstructionConstants.OP_ACONST_NULL)); + } + + public InstructionSequenceBuilder iconst(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_0, constant)); + } + + public InstructionSequenceBuilder iconst_m1() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_M1)); + } + + public InstructionSequenceBuilder iconst_0() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_0)); + } + + public InstructionSequenceBuilder iconst_1() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_1)); + } + + public InstructionSequenceBuilder iconst_2() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_2)); + } + + public InstructionSequenceBuilder iconst_3() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_3)); + } + + public InstructionSequenceBuilder iconst_4() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_4)); + } + + public InstructionSequenceBuilder iconst_5() + { + return add(new SimpleInstruction(InstructionConstants.OP_ICONST_5)); + } + + public InstructionSequenceBuilder lconst(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_LCONST_0, constant)); + } + + public InstructionSequenceBuilder lconst_0() + { + return add(new SimpleInstruction(InstructionConstants.OP_LCONST_0)); + } + + public InstructionSequenceBuilder lconst_1() + { + return add(new SimpleInstruction(InstructionConstants.OP_LCONST_1)); + } + + public InstructionSequenceBuilder fconst(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_FCONST_0, constant)); + } + + public InstructionSequenceBuilder fconst_0() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCONST_0)); + } + + public InstructionSequenceBuilder fconst_1() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCONST_1)); + } + + public InstructionSequenceBuilder fconst_2() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCONST_2)); + } + + public InstructionSequenceBuilder dconst(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_DCONST_0, constant)); + } + + public InstructionSequenceBuilder dconst_0() + { + return add(new SimpleInstruction(InstructionConstants.OP_DCONST_0)); + } + + public InstructionSequenceBuilder dconst_1() + { + return add(new SimpleInstruction(InstructionConstants.OP_DCONST_1)); + } + + public InstructionSequenceBuilder bipush(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_BIPUSH, constant)); + } + + public InstructionSequenceBuilder sipush(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_SIPUSH, constant)); + } + + public InstructionSequenceBuilder ldc(int value) + { + return ldc_(constantPoolEditor.addIntegerConstant(value)); + } + + public InstructionSequenceBuilder ldc(float value) + { + return ldc_(constantPoolEditor.addFloatConstant(value)); + } + + public InstructionSequenceBuilder ldc(Object primitiveArray) + { + return ldc_(constantPoolEditor.addPrimitiveArrayConstant(primitiveArray)); + } + + public InstructionSequenceBuilder ldc(String string) + { + return ldc(string, null, null); + } + + public InstructionSequenceBuilder ldc(String string, Clazz referencedClass, Method referencedMember) + { + return ldc_(constantPoolEditor.addStringConstant(string, referencedClass, referencedMember)); + } + + public InstructionSequenceBuilder ldc(String className, Clazz referencedClass) + { + return ldc_(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public InstructionSequenceBuilder ldc_(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_LDC, constantIndex)); + } + + public InstructionSequenceBuilder ldc_w(int value) + { + return ldc_w_(constantPoolEditor.addIntegerConstant(value)); + } + + public InstructionSequenceBuilder ldc_w(float value) + { + // If we can shrink the instruction, we may not need to create a constant. + return ldc_w_(constantPoolEditor.addFloatConstant(value)); + } + + public InstructionSequenceBuilder ldc_w(String string) + { + return ldc_w(string, null, null); + } + + public InstructionSequenceBuilder ldc_w(String string, Clazz referencedClass, Method referencedMember) + { + return ldc_w_(constantPoolEditor.addStringConstant(string, referencedClass, referencedMember)); + } + + public InstructionSequenceBuilder ldc_w(String className, Clazz referencedClass) + { + return ldc_w_(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public InstructionSequenceBuilder ldc_w_(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_LDC_W, constantIndex)); + } + + public InstructionSequenceBuilder ldc2_w(long value) + { + // If we can shrink the instruction, we may not need to create a constant. + return ldc2_w(constantPoolEditor.addLongConstant(value)); + } + + public InstructionSequenceBuilder ldc2_w(double value) + { + // If we can shrink the instruction, we may not need to create a constant. + return ldc2_w(constantPoolEditor.addDoubleConstant(value)); + } + + public InstructionSequenceBuilder ldc2_w(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_LDC2_W, constantIndex)); + } + + public InstructionSequenceBuilder iload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD, variableIndex)); + } + + public InstructionSequenceBuilder lload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD, variableIndex)); + } + + public InstructionSequenceBuilder fload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD, variableIndex)); + } + + public InstructionSequenceBuilder dload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD, variableIndex)); + } + + public InstructionSequenceBuilder aload(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD, variableIndex)); + } + + public InstructionSequenceBuilder iload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD_0)); + } + + public InstructionSequenceBuilder iload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD_1)); + } + + public InstructionSequenceBuilder iload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD_2)); + } + + public InstructionSequenceBuilder iload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_ILOAD_3)); + } + + public InstructionSequenceBuilder lload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD_0)); + } + + public InstructionSequenceBuilder lload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD_1)); + } + + public InstructionSequenceBuilder lload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD_2)); + } + + public InstructionSequenceBuilder lload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_LLOAD_3)); + } + + public InstructionSequenceBuilder fload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD_0)); + } + + public InstructionSequenceBuilder fload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD_1)); + } + + public InstructionSequenceBuilder fload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD_2)); + } + + public InstructionSequenceBuilder fload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_FLOAD_3)); + } + + public InstructionSequenceBuilder dload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD_0)); + } + + public InstructionSequenceBuilder dload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD_1)); + } + + public InstructionSequenceBuilder dload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD_2)); + } + + public InstructionSequenceBuilder dload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_DLOAD_3)); + } + + public InstructionSequenceBuilder aload_0() + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD_0)); + } + + public InstructionSequenceBuilder aload_1() + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD_1)); + } + + public InstructionSequenceBuilder aload_2() + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD_2)); + } + + public InstructionSequenceBuilder aload_3() + { + return add(new VariableInstruction(InstructionConstants.OP_ALOAD_3)); + } + + public InstructionSequenceBuilder iaload() + { + return add(new SimpleInstruction(InstructionConstants.OP_IALOAD)); + } + + public InstructionSequenceBuilder laload() + { + return add(new SimpleInstruction(InstructionConstants.OP_LALOAD)); + } + + public InstructionSequenceBuilder faload() + { + return add(new SimpleInstruction(InstructionConstants.OP_FALOAD)); + } + + public InstructionSequenceBuilder daload() + { + return add(new SimpleInstruction(InstructionConstants.OP_DALOAD)); + } + + public InstructionSequenceBuilder aaload() + { + return add(new SimpleInstruction(InstructionConstants.OP_AALOAD)); + } + + public InstructionSequenceBuilder baload() + { + return add(new SimpleInstruction(InstructionConstants.OP_BALOAD)); + } + + public InstructionSequenceBuilder caload() + { + return add(new SimpleInstruction(InstructionConstants.OP_CALOAD)); + } + + public InstructionSequenceBuilder saload() + { + return add(new SimpleInstruction(InstructionConstants.OP_SALOAD)); + } + + public InstructionSequenceBuilder istore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex)); + } + + public InstructionSequenceBuilder lstore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE, variableIndex)); + } + + public InstructionSequenceBuilder fstore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE, variableIndex)); + } + + public InstructionSequenceBuilder dstore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE, variableIndex)); + } + + public InstructionSequenceBuilder astore(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex)); + } + + public InstructionSequenceBuilder istore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE_0)); + } + + public InstructionSequenceBuilder istore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE_1)); + } + + public InstructionSequenceBuilder istore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE_2)); + } + + public InstructionSequenceBuilder istore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_ISTORE_3)); + } + + public InstructionSequenceBuilder lstore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE_0)); + } + + public InstructionSequenceBuilder lstore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE_1)); + } + + public InstructionSequenceBuilder lstore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE_2)); + } + + public InstructionSequenceBuilder lstore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_LSTORE_3)); + } + + public InstructionSequenceBuilder fstore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE_0)); + } + + public InstructionSequenceBuilder fstore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE_1)); + } + + public InstructionSequenceBuilder fstore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE_2)); + } + + public InstructionSequenceBuilder fstore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_FSTORE_3)); + } + + public InstructionSequenceBuilder dstore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE_0)); + } + + public InstructionSequenceBuilder dstore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE_1)); + } + + public InstructionSequenceBuilder dstore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE_2)); + } + + public InstructionSequenceBuilder dstore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_DSTORE_3)); + } + + public InstructionSequenceBuilder astore_0() + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE_0)); + } + + public InstructionSequenceBuilder astore_1() + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE_1)); + } + + public InstructionSequenceBuilder astore_2() + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE_2)); + } + + public InstructionSequenceBuilder astore_3() + { + return add(new VariableInstruction(InstructionConstants.OP_ASTORE_3)); + } + + public InstructionSequenceBuilder iastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_IASTORE)); + } + + public InstructionSequenceBuilder lastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_LASTORE)); + } + + public InstructionSequenceBuilder fastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_FASTORE)); + } + + public InstructionSequenceBuilder dastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_DASTORE)); + } + + public InstructionSequenceBuilder aastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_AASTORE)); + } + + public InstructionSequenceBuilder bastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_BASTORE)); + } + + public InstructionSequenceBuilder castore() + { + return add(new SimpleInstruction(InstructionConstants.OP_CASTORE)); + } + + public InstructionSequenceBuilder sastore() + { + return add(new SimpleInstruction(InstructionConstants.OP_SASTORE)); + } + + public InstructionSequenceBuilder pop() + { + return add(new SimpleInstruction(InstructionConstants.OP_POP)); + } + + public InstructionSequenceBuilder pop2() + { + return add(new SimpleInstruction(InstructionConstants.OP_POP2)); + } + + public InstructionSequenceBuilder dup() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP)); + } + + public InstructionSequenceBuilder dup_x1() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP_X1)); + } + + public InstructionSequenceBuilder dup_x2() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP_X2)); + } + + public InstructionSequenceBuilder dup2() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP2)); + } + + public InstructionSequenceBuilder dup2_x1() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP2_X1)); + } + + public InstructionSequenceBuilder dup2_x2() + { + return add(new SimpleInstruction(InstructionConstants.OP_DUP2_X2)); + } + + public InstructionSequenceBuilder swap() + { + return add(new SimpleInstruction(InstructionConstants.OP_SWAP)); + } + + public InstructionSequenceBuilder iadd() + { + return add(new SimpleInstruction(InstructionConstants.OP_IADD)); + } + + public InstructionSequenceBuilder ladd() + { + return add(new SimpleInstruction(InstructionConstants.OP_LADD)); + } + + public InstructionSequenceBuilder fadd() + { + return add(new SimpleInstruction(InstructionConstants.OP_FADD)); + } + + public InstructionSequenceBuilder dadd() + { + return add(new SimpleInstruction(InstructionConstants.OP_DADD)); + } + + public InstructionSequenceBuilder isub() + { + return add(new SimpleInstruction(InstructionConstants.OP_ISUB)); + } + + public InstructionSequenceBuilder lsub() + { + return add(new SimpleInstruction(InstructionConstants.OP_LSUB)); + } + + public InstructionSequenceBuilder fsub() + { + return add(new SimpleInstruction(InstructionConstants.OP_FSUB)); + } + + public InstructionSequenceBuilder dsub() + { + return add(new SimpleInstruction(InstructionConstants.OP_DSUB)); + } + + public InstructionSequenceBuilder imul() + { + return add(new SimpleInstruction(InstructionConstants.OP_IMUL)); + } + + public InstructionSequenceBuilder lmul() + { + return add(new SimpleInstruction(InstructionConstants.OP_LMUL)); + } + + public InstructionSequenceBuilder fmul() + { + return add(new SimpleInstruction(InstructionConstants.OP_FMUL)); + } + + public InstructionSequenceBuilder dmul() + { + return add(new SimpleInstruction(InstructionConstants.OP_DMUL)); + } + + public InstructionSequenceBuilder idiv() + { + return add(new SimpleInstruction(InstructionConstants.OP_IDIV)); + } + + public InstructionSequenceBuilder ldiv() + { + return add(new SimpleInstruction(InstructionConstants.OP_LDIV)); + } + + public InstructionSequenceBuilder fdiv() + { + return add(new SimpleInstruction(InstructionConstants.OP_FDIV)); + } + + public InstructionSequenceBuilder ddiv() + { + return add(new SimpleInstruction(InstructionConstants.OP_DDIV)); + } + + public InstructionSequenceBuilder irem() + { + return add(new SimpleInstruction(InstructionConstants.OP_IREM)); + } + + public InstructionSequenceBuilder lrem() + { + return add(new SimpleInstruction(InstructionConstants.OP_LREM)); + } + + public InstructionSequenceBuilder frem() + { + return add(new SimpleInstruction(InstructionConstants.OP_FREM)); + } + + public InstructionSequenceBuilder drem() + { + return add(new SimpleInstruction(InstructionConstants.OP_DREM)); + } + + public InstructionSequenceBuilder ineg() + { + return add(new SimpleInstruction(InstructionConstants.OP_INEG)); + } + + public InstructionSequenceBuilder lneg() + { + return add(new SimpleInstruction(InstructionConstants.OP_LNEG)); + } + + public InstructionSequenceBuilder fneg() + { + return add(new SimpleInstruction(InstructionConstants.OP_FNEG)); + } + + public InstructionSequenceBuilder dneg() + { + return add(new SimpleInstruction(InstructionConstants.OP_DNEG)); + } + + public InstructionSequenceBuilder ishl() + { + return add(new SimpleInstruction(InstructionConstants.OP_ISHL)); + } + + public InstructionSequenceBuilder lshl() + { + return add(new SimpleInstruction(InstructionConstants.OP_LSHL)); + } + + public InstructionSequenceBuilder ishr() + { + return add(new SimpleInstruction(InstructionConstants.OP_ISHR)); + } + + public InstructionSequenceBuilder lshr() + { + return add(new SimpleInstruction(InstructionConstants.OP_LSHR)); + } + + public InstructionSequenceBuilder iushr() + { + return add(new SimpleInstruction(InstructionConstants.OP_IUSHR)); + } + + public InstructionSequenceBuilder lushr() + { + return add(new SimpleInstruction(InstructionConstants.OP_LUSHR)); + } + + public InstructionSequenceBuilder iand() + { + return add(new SimpleInstruction(InstructionConstants.OP_IAND)); + } + + public InstructionSequenceBuilder land() + { + return add(new SimpleInstruction(InstructionConstants.OP_LAND)); + } + + public InstructionSequenceBuilder ior() + { + return add(new SimpleInstruction(InstructionConstants.OP_IOR)); + } + + public InstructionSequenceBuilder lor() + { + return add(new SimpleInstruction(InstructionConstants.OP_LOR)); + } + + public InstructionSequenceBuilder ixor() + { + return add(new SimpleInstruction(InstructionConstants.OP_IXOR)); + } + + public InstructionSequenceBuilder lxor() + { + return add(new SimpleInstruction(InstructionConstants.OP_LXOR)); + } + + public InstructionSequenceBuilder iinc(int variableIndex, + int constant) + { + return add(new VariableInstruction(InstructionConstants.OP_IINC, variableIndex, constant)); + } + + public InstructionSequenceBuilder i2l() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2L)); + } + + public InstructionSequenceBuilder i2f() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2F)); + } + + public InstructionSequenceBuilder i2d() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2D)); + } + + public InstructionSequenceBuilder l2i() + { + return add(new SimpleInstruction(InstructionConstants.OP_L2I)); + } + + public InstructionSequenceBuilder l2f() + { + return add(new SimpleInstruction(InstructionConstants.OP_L2F)); + } + + public InstructionSequenceBuilder l2d() + { + return add(new SimpleInstruction(InstructionConstants.OP_L2D)); + } + + public InstructionSequenceBuilder f2i() + { + return add(new SimpleInstruction(InstructionConstants.OP_F2I)); + } + + public InstructionSequenceBuilder f2l() + { + return add(new SimpleInstruction(InstructionConstants.OP_F2L)); + } + + public InstructionSequenceBuilder f2d() + { + return add(new SimpleInstruction(InstructionConstants.OP_F2D)); + } + + public InstructionSequenceBuilder d2i() + { + return add(new SimpleInstruction(InstructionConstants.OP_D2I)); + } + + public InstructionSequenceBuilder d2l() + { + return add(new SimpleInstruction(InstructionConstants.OP_D2L)); + } + + public InstructionSequenceBuilder d2f() + { + return add(new SimpleInstruction(InstructionConstants.OP_D2F)); + } + + public InstructionSequenceBuilder i2b() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2B)); + } + + public InstructionSequenceBuilder i2c() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2C)); + } + + public InstructionSequenceBuilder i2s() + { + return add(new SimpleInstruction(InstructionConstants.OP_I2S)); + } + + public InstructionSequenceBuilder lcmp() + { + return add(new SimpleInstruction(InstructionConstants.OP_LCMP)); + } + + public InstructionSequenceBuilder fcmpl() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCMPL)); + } + + public InstructionSequenceBuilder fcmpg() + { + return add(new SimpleInstruction(InstructionConstants.OP_FCMPG)); + } + + public InstructionSequenceBuilder dcmpl() + { + return add(new SimpleInstruction(InstructionConstants.OP_DCMPL)); + } + + public InstructionSequenceBuilder dcmpg() + { + return add(new SimpleInstruction(InstructionConstants.OP_DCMPG)); + } + + public InstructionSequenceBuilder ifeq(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFEQ, branchOffset)); + } + + public InstructionSequenceBuilder ifne(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFNE, branchOffset)); + } + + public InstructionSequenceBuilder iflt(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFLT, branchOffset)); + } + + public InstructionSequenceBuilder ifge(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFGE, branchOffset)); + } + + public InstructionSequenceBuilder ifgt(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFGT, branchOffset)); + } + + public InstructionSequenceBuilder ifle(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFLE, branchOffset)); + } + + public InstructionSequenceBuilder ificmpeq(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPEQ, branchOffset)); + } + + public InstructionSequenceBuilder ificmpne(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPNE, branchOffset)); + } + + public InstructionSequenceBuilder ificmplt(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPLT, branchOffset)); + } + + public InstructionSequenceBuilder ificmpge(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPGE, branchOffset)); + } + + public InstructionSequenceBuilder ificmpgt(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPGT, branchOffset)); + } + + public InstructionSequenceBuilder ificmple(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFICMPLE, branchOffset)); + } + + public InstructionSequenceBuilder ifacmpeq(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFACMPEQ, branchOffset)); + } + + public InstructionSequenceBuilder ifacmpne(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFACMPNE, branchOffset)); + } + + public InstructionSequenceBuilder goto_(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_GOTO, branchOffset)); + } + + public InstructionSequenceBuilder jsr(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_JSR, branchOffset)); + } + + public InstructionSequenceBuilder ret(int variableIndex) + { + return add(new VariableInstruction(InstructionConstants.OP_RET, variableIndex)); + } + + public InstructionSequenceBuilder tableswitch(int defaultOffset, + int lowCase, + int highCase, + int[] jumpOffsets) + { + return add(new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH, + defaultOffset, + lowCase, + highCase, + jumpOffsets)); + } + + public InstructionSequenceBuilder lookupswitch(int defaultOffset, + int[] cases, + int[] jumpOffsets) + { + return add(new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, + defaultOffset, + cases, + jumpOffsets)); + } + + public InstructionSequenceBuilder ireturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_IRETURN)); + } + + public InstructionSequenceBuilder lreturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_LRETURN)); + } + + public InstructionSequenceBuilder freturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_FRETURN)); + } + + public InstructionSequenceBuilder dreturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_DRETURN)); + } + + public InstructionSequenceBuilder areturn() + { + return add(new SimpleInstruction(InstructionConstants.OP_ARETURN)); + } + + public InstructionSequenceBuilder return_() + { + return add(new SimpleInstruction(InstructionConstants.OP_RETURN)); + } + + public InstructionSequenceBuilder getstatic(Clazz referencedClass, + Member referencedMember) + { + return getstatic(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder getstatic(String className, + String name, + String descriptor) + { + return getstatic(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder getstatic(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return getstatic(constantPoolEditor.addFieldrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder getstatic(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_GETSTATIC, constantIndex)); + } + + public InstructionSequenceBuilder putstatic(Clazz referencedClass, + Member referencedMember) + { + return putstatic(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder putstatic(String className, + String name, + String descriptor) + { + return putstatic(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder putstatic(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return putstatic(constantPoolEditor.addFieldrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder putstatic(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, constantIndex)); + } + + public InstructionSequenceBuilder getfield(Clazz referencedClass, + Member referencedMember) + { + return getfield(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder getfield(String className, + String name, + String descriptor) + { + return getfield(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder getfield(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return getfield(constantPoolEditor.addFieldrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder getfield(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_GETFIELD, constantIndex)); + } + + public InstructionSequenceBuilder putfield(Clazz referencedClass, + Member referencedMember) + { + return putfield(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder putfield(String className, + String name, + String descriptor) + { + return putfield(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder putfield(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return putfield(constantPoolEditor.addFieldrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder putfield(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_PUTFIELD, constantIndex)); + } + + public InstructionSequenceBuilder invokevirtual(Clazz referencedClass, + Member referencedMember) + { + return invokevirtual(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder invokevirtual(String className, + String name, + String descriptor) + { + return invokevirtual(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder invokevirtual(int classIndex, + String name, + String descriptor) + { + return invokevirtual(constantPoolEditor.addMethodrefConstant(classIndex, + name, + descriptor, + null, + null)); + } + + public InstructionSequenceBuilder invokevirtual(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokevirtual(constantPoolEditor.addMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder invokevirtual(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, constantIndex)); + } + + public InstructionSequenceBuilder invokespecial(String className, + String name, + String descriptor) + { + return invokespecial(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder invokespecial(Clazz referencedClass, + Member referencedMember) + { + return invokespecial(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder invokespecial(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokespecial(constantPoolEditor.addMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder invokespecial_interface(String className, + String name, + String descriptor) + { + return invokespecial_interface(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder invokespecial_interface(Clazz referencedClass, + Member referencedMember) + { + return invokespecial_interface(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder invokespecial_interface(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokespecial(constantPoolEditor.addInterfaceMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder invokespecial(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, constantIndex)); + } + + public InstructionSequenceBuilder invokestatic(Clazz referencedClass, + Member referencedMember) + { + return invokestatic(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder invokestatic(String className, + String name, + String descriptor) + { + return invokestatic(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder invokestatic(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokestatic(constantPoolEditor.addMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder invokestatic_interface(String className, + String name, + String descriptor) + { + return invokestatic_interface(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder invokestatic_interface(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + return invokestatic(constantPoolEditor.addInterfaceMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember)); + } + + public InstructionSequenceBuilder invokestatic(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, constantIndex)); + } + + public InstructionSequenceBuilder invokeinterface(Clazz referencedClass, + Member referencedMember) + { + return invokeinterface(referencedClass.getName(), + referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass), + referencedClass, + referencedMember); + } + + public InstructionSequenceBuilder invokeinterface(String className, + String name, + String descriptor) + { + return invokeinterface(className, name, descriptor, null, null); + } + + public InstructionSequenceBuilder invokeinterface(String className, + String name, + String descriptor, + Clazz referencedClass, + Member referencedMember) + { + int invokeinterfaceConstant = + (ClassUtil.internalMethodParameterSize(descriptor, false)) << 8; + + return invokeinterface(constantPoolEditor.addInterfaceMethodrefConstant(className, + name, + descriptor, + referencedClass, + referencedMember), + invokeinterfaceConstant); + } + + public InstructionSequenceBuilder invokeinterface(int constantIndex, int constant) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKEINTERFACE, constantIndex, constant)); + } + + public InstructionSequenceBuilder invokedynamic(int bootStrapMethodIndex, + String name, + String descriptor) + { + return invokedynamic(bootStrapMethodIndex, name, descriptor, null); + } + + public InstructionSequenceBuilder invokedynamic(int bootStrapMethodIndex, + String name, + String descriptor, + Clazz[] referencedClasses) + { + return invokedynamic(constantPoolEditor.addInvokeDynamicConstant(bootStrapMethodIndex, + name, + descriptor, + referencedClasses)); + } + + public InstructionSequenceBuilder invokedynamic(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INVOKEDYNAMIC, constantIndex)); + } + + public InstructionSequenceBuilder new_(String className) + { + return new_(className, null); + } + + public InstructionSequenceBuilder new_(String className, Clazz referencedClass) + { + return new_(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public InstructionSequenceBuilder new_(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_NEW, constantIndex)); + } + + public InstructionSequenceBuilder newarray(int constant) + { + return add(new SimpleInstruction(InstructionConstants.OP_NEWARRAY, constant)); + } + + public InstructionSequenceBuilder anewarray(String className, Clazz referencedClass) + { + return anewarray(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public InstructionSequenceBuilder anewarray(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, constantIndex)); + } + + public InstructionSequenceBuilder arraylength() + { + return add(new SimpleInstruction(InstructionConstants.OP_ARRAYLENGTH)); + } + + public InstructionSequenceBuilder athrow() + { + return add(new SimpleInstruction(InstructionConstants.OP_ATHROW)); + } + + public InstructionSequenceBuilder checkcast(String className) + { + return checkcast(className, null); + } + + public InstructionSequenceBuilder checkcast(String className, Clazz referencedClass) + { + return checkcast(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public InstructionSequenceBuilder checkcast(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_CHECKCAST, constantIndex)); + } + + public InstructionSequenceBuilder instanceof_(String className, Clazz referencedClass) + { + return instanceof_(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public InstructionSequenceBuilder instanceof_(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_INSTANCEOF, constantIndex)); + } + + public InstructionSequenceBuilder monitorenter() + { + return add(new SimpleInstruction(InstructionConstants.OP_MONITORENTER)); + } + + public InstructionSequenceBuilder monitorexit() + { + return add(new SimpleInstruction(InstructionConstants.OP_MONITOREXIT)); + } + + public InstructionSequenceBuilder wide() + { + return add(new SimpleInstruction(InstructionConstants.OP_WIDE)); + } + + public InstructionSequenceBuilder multianewarray(String className, Clazz referencedClass) + { + return multianewarray(constantPoolEditor.addClassConstant(className, referencedClass)); + } + + public InstructionSequenceBuilder multianewarray(int constantIndex) + { + return add(new ConstantInstruction(InstructionConstants.OP_MULTIANEWARRAY, constantIndex)); + } + + public InstructionSequenceBuilder ifnull(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFNULL, branchOffset)); + } + + public InstructionSequenceBuilder ifnonnull(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_IFNONNULL, branchOffset)); + } + + public InstructionSequenceBuilder goto_w(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_GOTO_W, branchOffset)); + } + + public InstructionSequenceBuilder jsr_w(int branchOffset) + { + return add(new BranchInstruction(InstructionConstants.OP_JSR_W, branchOffset)); + } + + + // Additional convenience methods. + + /** + * Pushes the given primitive value on the stack. + * + * Operand stack: + * ... -> ..., value + * + * @param value the primitive value to be pushed - should never be null. + * @param type the internal type of the primitive ('Z','B','I',...) + */ + public InstructionSequenceBuilder pushPrimitive(Object value, + char type) + { + switch (type) + { + case ClassConstants.TYPE_BOOLEAN: return ((Boolean)value).booleanValue() ? iconst_1() : iconst_0(); + case ClassConstants.TYPE_BYTE: + case ClassConstants.TYPE_SHORT: + case ClassConstants.TYPE_INT: return pushInt(((Number)value).intValue()); + case ClassConstants.TYPE_CHAR: return ldc(((Character)value).charValue()); + case ClassConstants.TYPE_LONG: return ldc2_w((Long)value); + case ClassConstants.TYPE_FLOAT: return ldc(((Float)value).floatValue()); + case ClassConstants.TYPE_DOUBLE: return ldc2_w((Double)value); + default: throw new IllegalArgumentException("" + type); + } + } + + + /** + * Pushes the given primitive int on the stack in the most efficient way + * (as an iconst, bipush, sipush, or ldc instruction). + * + * @param value the int value to be pushed. + */ + public InstructionSequenceBuilder pushInt(int value) + { + return + value >= -1 && + value <= 5 ? iconst(value) : + value == (byte)value ? bipush(value) : + value == (short)value ? sipush(value) : + ldc(value); + } + + + /** + * Pushes the given primitive float on the stack in the most efficient way + * (as an fconst or ldc instruction). + * + * @param value the int value to be pushed. + */ + public InstructionSequenceBuilder pushFloat(float value) + { + return + value == 0f || + value == 1f ? fconst((int)value) : + ldc(value); + } + + + /** + * Pushes the given primitive long on the stack in the most efficient way + * (as an lconst or ldc instruction). + * + * @param value the int value to be pushed. + */ + public InstructionSequenceBuilder pushLong(long value) + { + return + value == 0L || + value == 1L ? lconst((int)value) : + ldc2_w(value); + } + + + /** + * Pushes the given primitive double on the stack in the most efficient way + * (as a dconst or ldc instruction). + * + * @param value the int value to be pushed. + */ + public InstructionSequenceBuilder pushDouble(double value) + { + return + value == 0. || + value == 1. ? dconst((int)value) : + ldc2_w(value); + } + + + /** + * Pushes a new array on the stack. + * + * Operand stack: + * ... -> ..., array + * + * @param type the array element type (or class name in case of objects). + * @param size the size of the array to be created. + */ + public InstructionSequenceBuilder pushNewArray(String type, + int size) + { + // Create new array. + pushInt(size); + + return ClassUtil.isInternalPrimitiveType(type) ? + newarray(InstructionUtil.arrayTypeFromInternalType(type.charAt(0))) : + anewarray(type, null); + } + + + /** + * Loads the given variable onto the stack. + * + * Operand stack: + * ... -> ..., value + * + * @param variableIndex the index of the variable to be loaded. + * @param type the type of the variable to be loaded. + */ + public InstructionSequenceBuilder load(int variableIndex, + String type) + { + return load(variableIndex, type.charAt(0)); + } + + + /** + * Loads the given variable of primitive type onto the stack. + * + * Operand stack: + * ... -> ..., value + * + * @param variableIndex the index of the variable to be loaded. + * @param type the type of the variable to be loaded. + */ + public InstructionSequenceBuilder load(int variableIndex, + char type) + { + switch (type) + { + case ClassConstants.TYPE_BOOLEAN: + case ClassConstants.TYPE_BYTE: + case ClassConstants.TYPE_CHAR: + case ClassConstants.TYPE_SHORT: + case ClassConstants.TYPE_INT: return iload(variableIndex); + case ClassConstants.TYPE_LONG: return lload(variableIndex); + case ClassConstants.TYPE_FLOAT: return fload(variableIndex); + case ClassConstants.TYPE_DOUBLE: return dload(variableIndex); + default: return aload(variableIndex); + } + } + + + /** + * Stores the value on top of the stack in the variable with given index. + * + * Operand stsack: + * ..., value -> ... + * + * @param variableIndex the index of the variable where to store the + * value. + * @param type the type of the value to be stored. + */ + public InstructionSequenceBuilder store(int variableIndex, + String type) + { + return store(variableIndex, type.charAt(0)); + } + + + /** + * Stores the primitve value on top of the stack in the variable with given + * index. + * + * Operand stack: + * ..., value -> ... + * + * @param variableIndex the index of the variable where to store the + * value. + * @param type the type of the value to be stored. + */ + public InstructionSequenceBuilder store(int variableIndex, + char type) + { + switch (type) + { + case ClassConstants.TYPE_BOOLEAN: + case ClassConstants.TYPE_BYTE: + case ClassConstants.TYPE_CHAR: + case ClassConstants.TYPE_SHORT: + case ClassConstants.TYPE_INT: return istore(variableIndex); + case ClassConstants.TYPE_LONG: return lstore(variableIndex); + case ClassConstants.TYPE_FLOAT: return fstore(variableIndex); + case ClassConstants.TYPE_DOUBLE: return dstore(variableIndex); + default: return astore(variableIndex); + } + } + + + /** + * Stores an element to an array. + * + * Operand stack: + * ..., array, index, value -> ... + * + * @param elementType the type of the value to be stored. + */ + public InstructionSequenceBuilder storeToArray(String elementType) + { + // Store element on stack in array. + switch (elementType.charAt(0)) + { + case ClassConstants.TYPE_BOOLEAN: + case ClassConstants.TYPE_BYTE: return bastore(); + case ClassConstants.TYPE_CHAR: return castore(); + case ClassConstants.TYPE_SHORT: return sastore(); + case ClassConstants.TYPE_INT: return iastore(); + case ClassConstants.TYPE_LONG: return lastore(); + case ClassConstants.TYPE_FLOAT: return fastore(); + case ClassConstants.TYPE_DOUBLE: return dastore(); + default: return aastore(); + } + } + + + // Small utility methods. + + /** + * Adds the given instruction, shrinking it if necessary. + */ + private InstructionSequenceBuilder add(Instruction instruction) + { + instructions.add(instruction); + + return this; + } + + + /** + * This main method tests the class. + */ + public static void main(String[] args) + { + InstructionSequenceBuilder builder = new InstructionSequenceBuilder(); + + Instruction[] instructions = builder + .iconst_2() + .istore_0() + .iinc(0, 2) + .iload_0() + .ldc(12) + .iadd() + .putstatic("com/example/SomeClass", "someField", "I", null, null) + .instructions(); + + Constant[] constants = builder.constants(); + + for (Instruction instruction : instructions) + { + System.out.println(instruction); + } + + System.out.println(); + + for (int index = 0; index < constants.length; index++) + { + System.out.println("#"+index+": " + constants[index]); + } + } + + + /** + * This ProgramClass is a dummy container for a constant pool, with a null name. + */ + private static class MyDummyClass + extends ProgramClass + { + public MyDummyClass() + { + super(ClassConstants.CLASS_VERSION_1_0, 1, new Constant[256], 0, 0, 0); + } + + + // Overriding methods for Claaz. + + public String getName() + { + return null; + } + } +} diff --git a/src/proguard/classfile/editor/InstructionWriter.java b/core/src/proguard/classfile/editor/InstructionWriter.java similarity index 99% rename from src/proguard/classfile/editor/InstructionWriter.java rename to core/src/proguard/classfile/editor/InstructionWriter.java index c0174f386..98ac6222e 100644 --- a/src/proguard/classfile/editor/InstructionWriter.java +++ b/core/src/proguard/classfile/editor/InstructionWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/InterfaceAdder.java b/core/src/proguard/classfile/editor/InterfaceAdder.java similarity index 97% rename from src/proguard/classfile/editor/InterfaceAdder.java rename to core/src/proguard/classfile/editor/InterfaceAdder.java index 7cc8de873..4b634fd99 100644 --- a/src/proguard/classfile/editor/InterfaceAdder.java +++ b/core/src/proguard/classfile/editor/InterfaceAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/InterfaceDeleter.java b/core/src/proguard/classfile/editor/InterfaceDeleter.java similarity index 99% rename from src/proguard/classfile/editor/InterfaceDeleter.java rename to core/src/proguard/classfile/editor/InterfaceDeleter.java index 5cbf068fa..5260a258f 100644 --- a/src/proguard/classfile/editor/InterfaceDeleter.java +++ b/core/src/proguard/classfile/editor/InterfaceDeleter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/InterfaceSorter.java b/core/src/proguard/classfile/editor/InterfaceSorter.java similarity index 99% rename from src/proguard/classfile/editor/InterfaceSorter.java rename to core/src/proguard/classfile/editor/InterfaceSorter.java index 41cef213f..e4a29613f 100644 --- a/src/proguard/classfile/editor/InterfaceSorter.java +++ b/core/src/proguard/classfile/editor/InterfaceSorter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/InterfacesEditor.java b/core/src/proguard/classfile/editor/InterfacesEditor.java similarity index 98% rename from src/proguard/classfile/editor/InterfacesEditor.java rename to core/src/proguard/classfile/editor/InterfacesEditor.java index 7d0599b20..a98749928 100644 --- a/src/proguard/classfile/editor/InterfacesEditor.java +++ b/core/src/proguard/classfile/editor/InterfacesEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/LineNumberInfoAdder.java b/core/src/proguard/classfile/editor/LineNumberInfoAdder.java similarity index 98% rename from src/proguard/classfile/editor/LineNumberInfoAdder.java rename to core/src/proguard/classfile/editor/LineNumberInfoAdder.java index f499d294f..c85aa9a8f 100644 --- a/src/proguard/classfile/editor/LineNumberInfoAdder.java +++ b/core/src/proguard/classfile/editor/LineNumberInfoAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java b/core/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java similarity index 97% rename from src/proguard/classfile/editor/LineNumberTableAttributeEditor.java rename to core/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java index 3eded4e92..023ff2d11 100644 --- a/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java +++ b/core/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/LineNumberTableAttributeTrimmer.java b/core/src/proguard/classfile/editor/LineNumberTableAttributeTrimmer.java similarity index 98% rename from src/proguard/classfile/editor/LineNumberTableAttributeTrimmer.java rename to core/src/proguard/classfile/editor/LineNumberTableAttributeTrimmer.java index 8d860c31b..0421bbf50 100644 --- a/src/proguard/classfile/editor/LineNumberTableAttributeTrimmer.java +++ b/core/src/proguard/classfile/editor/LineNumberTableAttributeTrimmer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/LocalVariableInfoAdder.java b/core/src/proguard/classfile/editor/LocalVariableInfoAdder.java similarity index 98% rename from src/proguard/classfile/editor/LocalVariableInfoAdder.java rename to core/src/proguard/classfile/editor/LocalVariableInfoAdder.java index 909dc8f74..459abef8f 100644 --- a/src/proguard/classfile/editor/LocalVariableInfoAdder.java +++ b/core/src/proguard/classfile/editor/LocalVariableInfoAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java b/core/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java similarity index 97% rename from src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java rename to core/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java index 360d485f1..ba26663f1 100644 --- a/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java +++ b/core/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java b/core/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java similarity index 98% rename from src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java rename to core/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java index eec3c024c..549477b33 100644 --- a/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java +++ b/core/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java b/core/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java similarity index 97% rename from src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java rename to core/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java index 5e4d6e0e7..abaae01ca 100644 --- a/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java +++ b/core/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/MemberAdder.java b/core/src/proguard/classfile/editor/MemberAdder.java similarity index 79% rename from src/proguard/classfile/editor/MemberAdder.java rename to core/src/proguard/classfile/editor/MemberAdder.java index 2bd953d7d..4da4c5614 100644 --- a/src/proguard/classfile/editor/MemberAdder.java +++ b/core/src/proguard/classfile/editor/MemberAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,6 +24,7 @@ import proguard.classfile.attribute.Attribute; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.MemberVisitor; +import proguard.util.*; /** * This MemberVisitor copies all class members that it visits to the given @@ -46,11 +47,10 @@ public class MemberAdder private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; - private final ProgramClass targetClass; -// private final boolean addFields; - private final MemberVisitor extraMemberVisitor; + private final ProgramClass targetClass; + private final StringTransformer nameTransformer; + private final MemberVisitor extraMemberVisitor; - private final ConstantAdder constantAdder; private final ClassEditor classEditor; private final ConstantPoolEditor constantPoolEditor; @@ -76,17 +76,32 @@ public MemberAdder(ProgramClass targetClass) * 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) + public MemberAdder(ProgramClass targetClass, + MemberVisitor extraMemberVisitor) + { + this(targetClass, null, extraMemberVisitor); + } + + /** + * 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 nameTransformer the transformer to be applied to each member's + * name before copying. If null, the original + * name will be used. + * @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. + */ + public MemberAdder(ProgramClass targetClass, + StringTransformer nameTransformer, + MemberVisitor extraMemberVisitor) { this.targetClass = targetClass; -// this.addFields = addFields; + this.nameTransformer = nameTransformer; this.extraMemberVisitor = extraMemberVisitor; - constantAdder = new ConstantAdder(targetClass); classEditor = new ClassEditor(targetClass); constantPoolEditor = new ConstantPoolEditor(targetClass); } @@ -96,10 +111,15 @@ public MemberAdder(ProgramClass targetClass, public void visitProgramField(ProgramClass programClass, ProgramField programField) { - //String name = programField.getName(programClass); - //String descriptor = programField.getDescriptor(programClass); + String name = programField.getName(programClass); + String descriptor = programField.getDescriptor(programClass); int accessFlags = programField.getAccessFlags(); + if (nameTransformer != null) + { + name = nameTransformer.transform(name); + } + // 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. @@ -113,14 +133,15 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie // (ClassConstants.ACC_PRIVATE | // ClassConstants.ACC_STATIC)) != 0) // { + // // Rename the private or static field. + // String newName = newUniqueMemberName(name, targetClass.getName()); + // // if (DEBUG) // { - // System.out.println("MemberAdder: renaming field ["+targetClass+"."+targetField.getName(targetClass)+" "+targetField.getDescriptor(targetClass)+"]"); + // System.out.println("MemberAdder: renaming field ["+targetClass.getName()+"."+name+" "+descriptor+"] to ["+newName+"]"); // } // - // // Rename the private or static field. - // targetField.u2nameIndex = - // constantPoolEditor.addUtf8Constant(newUniqueMemberName(name, targetClass.getName())); + // targetField.u2nameIndex = constantPoolEditor.addUtf8Constant(newName); // } // else // { @@ -153,8 +174,8 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie // Create a copy of the field. ProgramField newProgramField = new ProgramField(accessFlags, - constantAdder.addConstant(programClass, programField.u2nameIndex), - constantAdder.addConstant(programClass, programField.u2descriptorIndex), + constantPoolEditor.addUtf8Constant(name), + constantPoolEditor.addUtf8Constant(descriptor), 0, programField.u2attributesCount > 0 ? new Attribute[programField.u2attributesCount] : @@ -187,6 +208,11 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM String descriptor = programMethod.getDescriptor(programClass); int accessFlags = programMethod.getAccessFlags(); + if (nameTransformer != null) + { + name = nameTransformer.transform(name); + } + // Does the target class already have such a method? ProgramMethod targetMethod = (ProgramMethod)targetClass.findMethod(name, descriptor); if (targetMethod != null) @@ -197,7 +223,7 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM // Keep the target method. if (DEBUG) { - System.out.println("MemberAdder: skipping abstract method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + System.out.println("MemberAdder: skipping abstract method ["+programClass.getName()+"."+name+descriptor+"] into ["+targetClass.getName()+"]"); } // Don't add a new method. @@ -212,7 +238,7 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM // 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()+"]"); + System.out.println("MemberAdder: updating method ["+programClass.getName()+"."+name+descriptor+"] into ["+targetClass.getName()+"]"); } // Replace the access flags. @@ -231,7 +257,7 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM if (DEBUG) { - System.out.println("MemberAdder: renaming method ["+targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); + System.out.println("MemberAdder: renaming method ["+targetClass.getName()+"."+name+descriptor+"]"); } // TODO: Handle non-abstract method with the same name and descriptor in the target class. @@ -244,21 +270,19 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM if (DEBUG) { - System.out.println("MemberAdder: copying method ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] into ["+targetClass.getName()+"]"); + System.out.println("MemberAdder: copying method ["+programClass.getName()+"."+name+descriptor+"] into ["+targetClass.getName()+"]"); } // Create a copy of the method. ProgramMethod newProgramMethod = new ProgramMethod(accessFlags & ~ClassConstants.ACC_FINAL, - constantAdder.addConstant(programClass, programMethod.u2nameIndex), - constantAdder.addConstant(programClass, programMethod.u2descriptorIndex), + constantPoolEditor.addUtf8Constant(name), + constantPoolEditor.addUtf8Constant(descriptor), 0, programMethod.u2attributesCount > 0 ? new Attribute[programMethod.u2attributesCount] : EMPTY_ATTRIBUTES, - programMethod.referencedClasses != null ? - (Clazz[])programMethod.referencedClasses.clone() : - null); + ArrayUtil.cloneOrNull(programMethod.referencedClasses)); // Link to its visitor info. newProgramMethod.setVisitorInfo(programMethod); diff --git a/src/proguard/classfile/editor/MemberReferenceFixer.java b/core/src/proguard/classfile/editor/MemberReferenceFixer.java similarity index 99% rename from src/proguard/classfile/editor/MemberReferenceFixer.java rename to core/src/proguard/classfile/editor/MemberReferenceFixer.java index 107205c57..4c8324f7a 100644 --- a/src/proguard/classfile/editor/MemberReferenceFixer.java +++ b/core/src/proguard/classfile/editor/MemberReferenceFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/editor/MemberRemover.java b/core/src/proguard/classfile/editor/MemberRemover.java new file mode 100644 index 000000000..ebb2d1dc7 --- /dev/null +++ b/core/src/proguard/classfile/editor/MemberRemover.java @@ -0,0 +1,95 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +import java.util.*; + + +/** + * This visitor removes all members it visits in a ProgramClass. + * + * It should be used in two steps: + * - in the first step, the collection step, all program fields to be removed + * should be visited. + * - in the second step, the removal step, the program class containing the + * program fields should be visited. This will actually delete all + * collected fields. + * + * For example, to remove all fields in a program class: + * + * MemberRemover remover = new MemberRemover(); + * programClass.fieldsAccept(remover); + * programClass.accept(remover); + * + * @author Johan Leys + */ +public class MemberRemover +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor +{ + private Set methodsToRemove = new HashSet(); + private Set fieldsToRemove = new HashSet(); + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) {} + + + public void visitProgramClass(ProgramClass programClass) + { + ClassEditor classEditor = new ClassEditor(programClass); + + // Remove all collected methods. + for (Method method : methodsToRemove) { + classEditor.removeMethod(method); + } + methodsToRemove.clear(); + + // Remove all collected fields. + for (Field field : fieldsToRemove) { + classEditor.removeField(field); + } + fieldsToRemove.clear(); + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) {} + + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + fieldsToRemove.add(programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + methodsToRemove.add(programMethod); + } +} diff --git a/src/proguard/classfile/editor/MethodInvocationFixer.java b/core/src/proguard/classfile/editor/MethodInvocationFixer.java similarity index 99% rename from src/proguard/classfile/editor/MethodInvocationFixer.java rename to core/src/proguard/classfile/editor/MethodInvocationFixer.java index 29fb9441c..0dce15cc1 100644 --- a/src/proguard/classfile/editor/MethodInvocationFixer.java +++ b/core/src/proguard/classfile/editor/MethodInvocationFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/NameAndTypeShrinker.java b/core/src/proguard/classfile/editor/NameAndTypeShrinker.java similarity index 99% rename from src/proguard/classfile/editor/NameAndTypeShrinker.java rename to core/src/proguard/classfile/editor/NameAndTypeShrinker.java index 6a21e0770..6333a25e6 100644 --- a/src/proguard/classfile/editor/NameAndTypeShrinker.java +++ b/core/src/proguard/classfile/editor/NameAndTypeShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/NamedAttributeDeleter.java b/core/src/proguard/classfile/editor/NamedAttributeDeleter.java similarity index 97% rename from src/proguard/classfile/editor/NamedAttributeDeleter.java rename to core/src/proguard/classfile/editor/NamedAttributeDeleter.java index 90b617201..0f9b947f0 100644 --- a/src/proguard/classfile/editor/NamedAttributeDeleter.java +++ b/core/src/proguard/classfile/editor/NamedAttributeDeleter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java b/core/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java similarity index 97% rename from src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java rename to core/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java index 6dabf96e8..dc5ac497d 100644 --- a/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java +++ b/core/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/ParameterInfoAdder.java b/core/src/proguard/classfile/editor/ParameterInfoAdder.java similarity index 89% rename from src/proguard/classfile/editor/ParameterInfoAdder.java rename to core/src/proguard/classfile/editor/ParameterInfoAdder.java index dd729239e..22e399b5b 100644 --- a/src/proguard/classfile/editor/ParameterInfoAdder.java +++ b/core/src/proguard/classfile/editor/ParameterInfoAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -52,9 +52,11 @@ public ParameterInfoAdder(ProgramClass targetClass, public void visitParameterInfo(Clazz clazz, Method method, int parameterIndex, ParameterInfo parameterInfo) { // Create a new parameter. + int newNameIndex = parameterInfo.u2nameIndex == 0 ? 0 : + constantAdder.addConstant(clazz, parameterInfo.u2nameIndex); + ParameterInfo newParameterInfo = - new ParameterInfo(constantAdder.addConstant(clazz, parameterInfo.u2nameIndex), - parameterInfo.u2accessFlags); + new ParameterInfo(newNameIndex, parameterInfo.u2accessFlags); // Add it to the target. targetMethodParametersAttribute.parameters[parameterIndex] = newParameterInfo; diff --git a/core/src/proguard/classfile/editor/SimplifiedClassEditor.java b/core/src/proguard/classfile/editor/SimplifiedClassEditor.java new file mode 100644 index 000000000..90bca8f79 --- /dev/null +++ b/core/src/proguard/classfile/editor/SimplifiedClassEditor.java @@ -0,0 +1,517 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.Constant; +import proguard.classfile.instruction.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.obfuscate.*; + +import java.util.*; + +import static proguard.classfile.ClassConstants.*; + +/** + * This editor allows to build and/or edit classes (ProgramClass instances). + * It provides methods to easily add fields and methods to classes. + * + * @author Johan Leys + */ +public class SimplifiedClassEditor +extends SimplifiedVisitor +implements + // Implementation interfaces. + AttributeVisitor +{ + private static final String EXTRA_INIT_METHOD_NAME = "init$"; + private static final String EXTRA_INIT_METHOD_DESCRIPTOR = "()V"; + + private final ProgramClass programClass; + private final ClassEditor classEditor; + private final ConstantPoolEditor constantPoolEditor; + private final NameFactory nameFactory; + + private String superClassName; + + private final List methodComposers = new ArrayList(); + + private Instruction[] instructions; + + /** + * Creates a new SimplifiedClassEditor for the Java class with the given + * name. + * + * @param u2accessFlags access flags for the new class. + * @param className the fully qualified name of the new class. + * + * @see ClassConstants + */ + public SimplifiedClassEditor(int u2accessFlags, String className) + { + this(u2accessFlags, className, null); + } + + + /** + * Creates a new SimplifiedClassEditor for the Java class with the given + * name and super class. + * + * @param u2accessFlags access flags for the new class. + * @param className the fully qualified name of the new class. + * @param superclassName the fully qualified name of the super class. + * + * @see ClassConstants + */ + public SimplifiedClassEditor(int u2accessFlags, + String className, + String superclassName) + { + this(new ProgramClass(ClassConstants.CLASS_VERSION_1_2, + 1, + new Constant[10], + u2accessFlags, + 0, + 0)); + + programClass.u2thisClass = + constantPoolEditor.addClassConstant(className, programClass); + + if (superclassName != null) + { + programClass.u2superClass = + constantPoolEditor.addClassConstant(superclassName, null); + this.superClassName = superclassName; + } + } + + + /** + * Creates a new SimplifiedClassEditor for the given class. + * + * @param programClass the class to be edited. + */ + public SimplifiedClassEditor(ProgramClass programClass) + { + this.programClass = programClass; + classEditor = new ClassEditor(programClass); + constantPoolEditor = new ConstantPoolEditor(programClass); + nameFactory = UniqueMemberNameFactory.newInjectedMemberNameFactory(programClass); + } + + + /** + * Finalizes the editing of the class. This method does not initialize + * references to/from related classes. + * At least one of the finishEditing methods should be called before + * calling {@link #getProgramClass}. + * + * @see #finishEditing(ClassPool, ClassPool) + */ + public void finishEditing() { + for (CodeComposer composer : methodComposers) { + composer.finishEditing(); + } + } + + /** + * Finalizes the editing of the class, and initializes all references + * of the edited class w.r.t. the given program and library class pool. + * At least one of the finishEditing methods should be called before + * calling {@link #getProgramClass}. + * + * @param programClassPool the program class pool + * @param libraryClassPool the library class pool + */ + public void finishEditing(ClassPool programClassPool, + ClassPool libraryClassPool) { + for (CodeComposer composer : methodComposers) { + composer.finishEditing(); + } + + // Initialize all references to/from the edited class. + if (superClassName != null) + { + new ClassSuperHierarchyInitializer(programClassPool, libraryClassPool, null, null).visitProgramClass(programClass); + new ClassSubHierarchyInitializer().visitProgramClass(programClass); + } + new ClassReferenceInitializer(programClassPool, libraryClassPool).visitProgramClass(programClass); + } + + /** + * Returns the edited ProgramClass instance. + * Make sure to call one of the finishEditing methods after finishing editing, + * before calling this method. + * + * @return the edited ProgramClass instance. + * + * @see #finishEditing() + * @see #finishEditing(ClassPool, ClassPool) + */ + public ProgramClass getProgramClass() + { + return programClass; + } + + + /** + * Adds the given class constant to the edited class. + * + * @param name the class name to be added. + * @param referencedClass the corresponding referenced class. + * + * @return the constant pool index of the ClassConstant. + */ + public int addClassConstant(String name, + Clazz referencedClass) { + return constantPoolEditor.addClassConstant(name, referencedClass); + } + + /** + * Adds a new field to the edited class. + * + * @param u2accessFlags acces flags for the new field. + * @param fieldName name of the new field. + * @param fieldDescriptor descriptor of the new field. + * + * @return this SimpleClassEditor. + */ + public SimplifiedClassEditor addField(int u2accessFlags, + String fieldName, + String fieldDescriptor) + { + Field field = new ProgramField(u2accessFlags, + constantPoolEditor.addUtf8Constant(fieldName), + constantPoolEditor.addUtf8Constant(fieldDescriptor), + null); + classEditor.addField(field); + return this; + } + + + /** + * Adds a new method to the edited class. The returned composer can be used + * to attach code to the method. + * + * @param u2accessFlags acces flags for the new method. + * @param methodName name of the new method. + * @param methodDescriptor descriptor of the new method. + * @param maxCodeFragmentLength maximum length for the code fragment of the + * new method. + * + * @return the composer for adding code to the created method. + */ + public CompactCodeAttributeComposer addMethod(int u2accessFlags, + String methodName, + String methodDescriptor, + int maxCodeFragmentLength) + { + return addMethod(u2accessFlags, methodName, methodDescriptor, null, maxCodeFragmentLength); + } + + /** + * Adds a new method to the edited class. The returned composer can be used + * to attach code to the method. + * + * @param u2accessFlags acces flags for the new method. + * @param methodName name of the new method. + * @param methodDescriptor descriptor of the new method. + * @param referencedClasses the classes referenced by the method descriptor. + * @param maxCodeFragmentLength maximum length for the code fragment of the + * new method. + * + * @return the composer for adding code to the created method. + */ + public CompactCodeAttributeComposer addMethod(int u2accessFlags, + String methodName, + String methodDescriptor, + Clazz[] referencedClasses, + int maxCodeFragmentLength) + { + ProgramMethod method = new ProgramMethod(u2accessFlags, + constantPoolEditor.addUtf8Constant(methodName), + constantPoolEditor.addUtf8Constant(methodDescriptor), + referencedClasses); + CodeComposer composer = new CodeComposer(method, maxCodeFragmentLength); + methodComposers.add(composer); + return composer; + } + + + /** + * Adds a new method to the edited class, with the given instructions array. + * + * @param u2accessFlags acces flags for the new method. + * @param methodName name of the new method. + * @param methodDescriptor descriptor of the new method. + * @param instructions the instructions of the new method. + */ + public ProgramMethod addMethod(int u2accessFlags, + String methodName, + String methodDescriptor, + Instruction[] instructions) + { + return addMethod(u2accessFlags, methodName, methodDescriptor, instructions, null, null); + } + + + /** + * Adds the given static initializer instructions to the edited class. + * If the class already contains a static initializer, the new instructions + * will be appended to the existing initializer. + * + * @param instructions the instructions to be added. + * @param mergeIntoExistingInitializer indicates whether the instructions should + * be added to the existing static initializer + * (if it exists), or if a new method should + * be created, which is then called from the + * existing initializer. + */ + public void addStaticInitializerInstructions(Instruction[] instructions, + boolean mergeIntoExistingInitializer) + { + Method method = programClass.findMethod(METHOD_NAME_CLINIT, METHOD_TYPE_CLINIT); + + if (method == null) { + addMethod(ACC_STATIC, METHOD_NAME_CLINIT, METHOD_TYPE_CLINIT, + instructions, null, + new SimpleInstruction(InstructionConstants.OP_RETURN)); + } + else { + if (!mergeIntoExistingInitializer) + { + // Create a new static initializer. + ProgramMethod newMethod = + addMethod(ACC_STATIC, + nameFactory.nextName(), "()V", + instructions, + null, + new SimpleInstruction(InstructionConstants.OP_RETURN)); + + // Call the new initializer from the existing one. + InstructionSequenceBuilder builder = new InstructionSequenceBuilder(programClass); + builder.invokestatic(programClass.getName(), + newMethod.getName(programClass), + "()V", + programClass, + newMethod); + instructions = builder.instructions(); + } + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + ((ProgramMethod) method).attributesAccept(programClass, + new CodeAttributeEditorResetter(codeAttributeEditor)); + codeAttributeEditor.insertBeforeOffset(0, instructions); + ((ProgramMethod) method).attributesAccept(programClass, + codeAttributeEditor); + } + } + + /** + * Adds the given initialization instructions to the edited class. + * + * - If the class doesn't contain a constructor yet, it will be created, + * and the instructions will be added to this constructor. + * - If there is a single super-calling constructor, the instructions will + * be added at the beginning of it's code attribute. + * - If there are multiple super-calling constructors, a new private + * parameterless helper method will be created, to which the instructions + * will be added. An invocation to this new method will be added at the + * beginning of the code attribute of all super-calling constructors. + * + * @param instructions the instructions to be added. + */ + public void addInitializerInstructions(Instruction[] instructions) + { + Method method = programClass.findMethod(METHOD_NAME_INIT, null); + + if (method == null) { + // First call the super constructor. + Instruction[] firstInstruction = { + new VariableInstruction(InstructionConstants.OP_ALOAD_0), + new ConstantInstruction( + InstructionConstants.OP_INVOKESPECIAL, + constantPoolEditor.addMethodrefConstant(programClass.getSuperClass().getName(), METHOD_NAME_INIT, METHOD_TYPE_INIT, null, null)) + }; + + // End by calling return. + SimpleInstruction lastInstruction = + new SimpleInstruction(InstructionConstants.OP_RETURN); + + addMethod(ACC_PUBLIC, METHOD_NAME_INIT, METHOD_TYPE_INIT, + instructions, + firstInstruction, + lastInstruction); + } + else { + // Find all super-calling constructors. + Set constructors = new HashSet(); + programClass.methodsAccept( + new ConstructorMethodFilter( + new MethodCollector(constructors), null, null)); + + if (constructors.size() == 1) + { + // There is only one supper-calling constructor. + // Add the code to this constructor. + this.instructions = instructions; + constructors.iterator().next().accept(programClass, + new AllAttributeVisitor( + this)); + } + else + { + // There are multiple super-calling constructors. Add the + // instructions to a separate, parameterless initialization + // method, and invoke this method from all super-calling + // constructors. + ProgramMethod initMethod = (ProgramMethod) programClass.findMethod(EXTRA_INIT_METHOD_NAME, + EXTRA_INIT_METHOD_DESCRIPTOR); + if (initMethod == null) + { + // There is no init$ method yet. Create it now, and add the + // given instructions to it. + initMethod = addMethod(ACC_PRIVATE, + EXTRA_INIT_METHOD_NAME, + EXTRA_INIT_METHOD_DESCRIPTOR, + instructions, + null, + new SimpleInstruction(InstructionConstants.OP_RETURN)); + + // Insert a call to the new init$ method in all super-calling constructors. + InstructionSequenceBuilder builder = new InstructionSequenceBuilder(programClass); + builder.aload_0(); + builder.invokespecial(programClass.getName(), + EXTRA_INIT_METHOD_NAME, + EXTRA_INIT_METHOD_DESCRIPTOR, + programClass, + initMethod); + this.instructions = builder.instructions(); + programClass.methodsAccept( + new ConstructorMethodFilter( + new AllAttributeVisitor( + this), null, null)); + } + else { + // There is already an init$ method. Add the instructions to this method. + this.instructions = instructions; + initMethod.accept(programClass, + new AllAttributeVisitor( + this)); + } + } + } + } + + + /** + * Adds a new method to the edited class, with the given instructions array. + * + * @param u2accessFlags acces flags for the new method. + * @param methodName name of the new method. + * @param methodDescriptor descriptor of the new method. + * @param instructions the instructions of the new method. + * @param firstInstructions extra instructions to add in front of the + * new method. + * @param lastInstruction extra instruction to add at the end of the + * new method. + */ + private ProgramMethod addMethod(int u2accessFlags, + String methodName, + String methodDescriptor, + Instruction[] instructions, + Instruction[] firstInstructions, + Instruction lastInstruction) + { + ProgramMethod method = new ProgramMethod(u2accessFlags, + constantPoolEditor.addUtf8Constant(methodName), + constantPoolEditor.addUtf8Constant(methodDescriptor), + null); + + CodeAttribute codeAttribute = + new CodeAttribute(constantPoolEditor.addUtf8Constant(ClassConstants.ATTR_Code)); + + CodeAttributeComposer composer = new CodeAttributeComposer(); + composer.reset(); + composer.beginCodeFragment(0); + composer.appendInstructions(instructions); + if (firstInstructions != null) { + for (Instruction instruction : firstInstructions) { + composer.appendInstruction(instruction); + } + } + if (lastInstruction != null) { + composer.appendInstruction(lastInstruction); + } + composer.endCodeFragment(); + composer.visitCodeAttribute(programClass, method, codeAttribute); + + new AttributesEditor(programClass, method, false).addAttribute(codeAttribute); + + classEditor.addMethod(method); + + return method; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + ((ProgramMethod) method).attributesAccept(programClass, new CodeAttributeEditorResetter(codeAttributeEditor)); + codeAttributeEditor.insertBeforeOffset(0, instructions); + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + + } + + + private class CodeComposer extends CompactCodeAttributeComposer { + + private final ProgramMethod method; + + public CodeComposer(ProgramMethod method, + int maxCodeFragmentLength) + { + super(programClass); + this.method = method; + beginCodeFragment(maxCodeFragmentLength); + } + + public void finishEditing() { + endCodeFragment(); + + CodeAttribute codeAttribute = + new CodeAttribute(constantPoolEditor.addUtf8Constant(ClassConstants.ATTR_Code)); + + visitCodeAttribute(programClass, method, codeAttribute); + + new AttributesEditor(programClass, method, false).addAttribute(codeAttribute); + + classEditor.addMethod(method); + } + } +} diff --git a/src/proguard/classfile/editor/StackSizeUpdater.java b/core/src/proguard/classfile/editor/StackSizeUpdater.java similarity index 96% rename from src/proguard/classfile/editor/StackSizeUpdater.java rename to core/src/proguard/classfile/editor/StackSizeUpdater.java index 625c53323..64a177b37 100644 --- a/src/proguard/classfile/editor/StackSizeUpdater.java +++ b/core/src/proguard/classfile/editor/StackSizeUpdater.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/SubclassAdder.java b/core/src/proguard/classfile/editor/SubclassAdder.java similarity index 96% rename from src/proguard/classfile/editor/SubclassAdder.java rename to core/src/proguard/classfile/editor/SubclassAdder.java index e1de014ba..d07cc6e46 100644 --- a/src/proguard/classfile/editor/SubclassAdder.java +++ b/core/src/proguard/classfile/editor/SubclassAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/SubclassToAdder.java b/core/src/proguard/classfile/editor/SubclassToAdder.java similarity index 96% rename from src/proguard/classfile/editor/SubclassToAdder.java rename to core/src/proguard/classfile/editor/SubclassToAdder.java index 0fa599e2e..2085399b8 100644 --- a/src/proguard/classfile/editor/SubclassToAdder.java +++ b/core/src/proguard/classfile/editor/SubclassToAdder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/Utf8Shrinker.java b/core/src/proguard/classfile/editor/Utf8Shrinker.java similarity index 99% rename from src/proguard/classfile/editor/Utf8Shrinker.java rename to core/src/proguard/classfile/editor/Utf8Shrinker.java index 1f563d5d2..9575c3a5c 100644 --- a/src/proguard/classfile/editor/Utf8Shrinker.java +++ b/core/src/proguard/classfile/editor/Utf8Shrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -172,6 +172,9 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { markCpUtf8Entry(clazz, deprecatedAttribute.u2attributeNameIndex); diff --git a/src/proguard/classfile/editor/VariableCleaner.java b/core/src/proguard/classfile/editor/VariableCleaner.java similarity index 99% rename from src/proguard/classfile/editor/VariableCleaner.java rename to core/src/proguard/classfile/editor/VariableCleaner.java index 991beb5cf..abb2b7dec 100644 --- a/src/proguard/classfile/editor/VariableCleaner.java +++ b/core/src/proguard/classfile/editor/VariableCleaner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/VariableEditor.java b/core/src/proguard/classfile/editor/VariableEditor.java similarity index 98% rename from src/proguard/classfile/editor/VariableEditor.java rename to core/src/proguard/classfile/editor/VariableEditor.java index 0126be2ee..73e8197af 100644 --- a/src/proguard/classfile/editor/VariableEditor.java +++ b/core/src/proguard/classfile/editor/VariableEditor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/VariableRemapper.java b/core/src/proguard/classfile/editor/VariableRemapper.java similarity index 99% rename from src/proguard/classfile/editor/VariableRemapper.java rename to core/src/proguard/classfile/editor/VariableRemapper.java index dd6febdea..26e93b4ef 100644 --- a/src/proguard/classfile/editor/VariableRemapper.java +++ b/core/src/proguard/classfile/editor/VariableRemapper.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/VariableSizeUpdater.java b/core/src/proguard/classfile/editor/VariableSizeUpdater.java similarity index 98% rename from src/proguard/classfile/editor/VariableSizeUpdater.java rename to core/src/proguard/classfile/editor/VariableSizeUpdater.java index d88077ed9..a4524eb81 100644 --- a/src/proguard/classfile/editor/VariableSizeUpdater.java +++ b/core/src/proguard/classfile/editor/VariableSizeUpdater.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/editor/package.html b/core/src/proguard/classfile/editor/package.html similarity index 100% rename from src/proguard/classfile/editor/package.html rename to core/src/proguard/classfile/editor/package.html diff --git a/src/proguard/classfile/instruction/BranchInstruction.java b/core/src/proguard/classfile/instruction/BranchInstruction.java similarity index 96% rename from src/proguard/classfile/instruction/BranchInstruction.java rename to core/src/proguard/classfile/instruction/BranchInstruction.java index 937fe7ebe..c0fa03a24 100644 --- a/src/proguard/classfile/instruction/BranchInstruction.java +++ b/core/src/proguard/classfile/instruction/BranchInstruction.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -41,6 +41,10 @@ public class BranchInstruction extends Instruction public BranchInstruction() {} + /** + * Creates a BranchInstruction with the given branch offset. + * The branch offset is relative to this instruction's offset. + */ public BranchInstruction(byte opcode, int branchOffset) { this.opcode = opcode; diff --git a/src/proguard/classfile/instruction/ConstantInstruction.java b/core/src/proguard/classfile/instruction/ConstantInstruction.java similarity index 96% rename from src/proguard/classfile/instruction/ConstantInstruction.java rename to core/src/proguard/classfile/instruction/ConstantInstruction.java index 0c6d5c559..8377cda70 100644 --- a/src/proguard/classfile/instruction/ConstantInstruction.java +++ b/core/src/proguard/classfile/instruction/ConstantInstruction.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -222,11 +222,14 @@ 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 visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) {} 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 visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) {} + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) {} public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) diff --git a/src/proguard/classfile/instruction/Instruction.java b/core/src/proguard/classfile/instruction/Instruction.java similarity index 99% rename from src/proguard/classfile/instruction/Instruction.java rename to core/src/proguard/classfile/instruction/Instruction.java index 5fe0bba61..2f8a304b4 100644 --- a/src/proguard/classfile/instruction/Instruction.java +++ b/core/src/proguard/classfile/instruction/Instruction.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/InstructionConstants.java b/core/src/proguard/classfile/instruction/InstructionConstants.java similarity index 99% rename from src/proguard/classfile/instruction/InstructionConstants.java rename to core/src/proguard/classfile/instruction/InstructionConstants.java index 27676877a..ac8e33b16 100644 --- a/src/proguard/classfile/instruction/InstructionConstants.java +++ b/core/src/proguard/classfile/instruction/InstructionConstants.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/InstructionFactory.java b/core/src/proguard/classfile/instruction/InstructionFactory.java similarity index 99% rename from src/proguard/classfile/instruction/InstructionFactory.java rename to core/src/proguard/classfile/instruction/InstructionFactory.java index 75291d16f..d3b985a0c 100644 --- a/src/proguard/classfile/instruction/InstructionFactory.java +++ b/core/src/proguard/classfile/instruction/InstructionFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/InstructionUtil.java b/core/src/proguard/classfile/instruction/InstructionUtil.java similarity index 73% rename from src/proguard/classfile/instruction/InstructionUtil.java rename to core/src/proguard/classfile/instruction/InstructionUtil.java index 96962d3e6..bd4f6a650 100644 --- a/src/proguard/classfile/instruction/InstructionUtil.java +++ b/core/src/proguard/classfile/instruction/InstructionUtil.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,6 +22,9 @@ import proguard.classfile.ClassConstants; +import static proguard.classfile.ClassConstants.*; +import static proguard.classfile.instruction.InstructionConstants.*; + /** * Utility methods for converting between representations of names and * descriptions. @@ -64,4 +67,29 @@ public static char internalTypeFromArrayType(byte arrayType) default: throw new IllegalArgumentException("Unknown array type ["+arrayType+"]"); } } + + /** + * Returns the newarray type constant for the given internal primitive + * type. + * + * @param internalType a primitive type ('Z','B','I',...) + * @return the array type constant corresponding to the given + * primitive type. + * @see #internalTypeFromArrayType(byte) + */ + public static byte arrayTypeFromInternalType(char internalType) + { + switch (internalType) + { + case TYPE_BOOLEAN: return ARRAY_T_BOOLEAN; + case TYPE_BYTE: return ARRAY_T_BYTE; + case TYPE_CHAR: return ARRAY_T_CHAR; + case TYPE_SHORT: return ARRAY_T_SHORT; + case TYPE_INT: return ARRAY_T_INT; + case TYPE_LONG: return ARRAY_T_LONG; + case TYPE_FLOAT: return ARRAY_T_FLOAT; + case TYPE_DOUBLE: return ARRAY_T_DOUBLE; + default: throw new IllegalArgumentException("Unknown primitive: " + internalType); + } + } } diff --git a/src/proguard/classfile/instruction/LookUpSwitchInstruction.java b/core/src/proguard/classfile/instruction/LookUpSwitchInstruction.java similarity index 98% rename from src/proguard/classfile/instruction/LookUpSwitchInstruction.java rename to core/src/proguard/classfile/instruction/LookUpSwitchInstruction.java index 0533b08b0..83dcac262 100644 --- a/src/proguard/classfile/instruction/LookUpSwitchInstruction.java +++ b/core/src/proguard/classfile/instruction/LookUpSwitchInstruction.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/SimpleInstruction.java b/core/src/proguard/classfile/instruction/SimpleInstruction.java similarity index 99% rename from src/proguard/classfile/instruction/SimpleInstruction.java rename to core/src/proguard/classfile/instruction/SimpleInstruction.java index 1f3932f81..543b57081 100644 --- a/src/proguard/classfile/instruction/SimpleInstruction.java +++ b/core/src/proguard/classfile/instruction/SimpleInstruction.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/SwitchInstruction.java b/core/src/proguard/classfile/instruction/SwitchInstruction.java similarity index 97% rename from src/proguard/classfile/instruction/SwitchInstruction.java rename to core/src/proguard/classfile/instruction/SwitchInstruction.java index d2a138c89..2cb8abef2 100644 --- a/src/proguard/classfile/instruction/SwitchInstruction.java +++ b/core/src/proguard/classfile/instruction/SwitchInstruction.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/TableSwitchInstruction.java b/core/src/proguard/classfile/instruction/TableSwitchInstruction.java similarity index 98% rename from src/proguard/classfile/instruction/TableSwitchInstruction.java rename to core/src/proguard/classfile/instruction/TableSwitchInstruction.java index 1231fe893..fc55f9cc1 100644 --- a/src/proguard/classfile/instruction/TableSwitchInstruction.java +++ b/core/src/proguard/classfile/instruction/TableSwitchInstruction.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/VariableInstruction.java b/core/src/proguard/classfile/instruction/VariableInstruction.java similarity index 99% rename from src/proguard/classfile/instruction/VariableInstruction.java rename to core/src/proguard/classfile/instruction/VariableInstruction.java index dd232cc58..c650abb82 100644 --- a/src/proguard/classfile/instruction/VariableInstruction.java +++ b/core/src/proguard/classfile/instruction/VariableInstruction.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/package.html b/core/src/proguard/classfile/instruction/package.html similarity index 100% rename from src/proguard/classfile/instruction/package.html rename to core/src/proguard/classfile/instruction/package.html diff --git a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java b/core/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java similarity index 97% rename from src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java rename to core/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java index b06b957fb..244c7ca06 100644 --- a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java +++ b/core/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java b/core/src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java similarity index 97% rename from src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java rename to core/src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java index 439c63531..50847d799 100644 --- a/src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java +++ b/core/src/proguard/classfile/instruction/visitor/InstructionConstantVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/visitor/InstructionCounter.java b/core/src/proguard/classfile/instruction/visitor/InstructionCounter.java similarity index 97% rename from src/proguard/classfile/instruction/visitor/InstructionCounter.java rename to core/src/proguard/classfile/instruction/visitor/InstructionCounter.java index a67b5f35b..a136d2422 100644 --- a/src/proguard/classfile/instruction/visitor/InstructionCounter.java +++ b/core/src/proguard/classfile/instruction/visitor/InstructionCounter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/visitor/InstructionVisitor.java b/core/src/proguard/classfile/instruction/visitor/InstructionVisitor.java similarity index 97% rename from src/proguard/classfile/instruction/visitor/InstructionVisitor.java rename to core/src/proguard/classfile/instruction/visitor/InstructionVisitor.java index 37596dd7b..697196800 100644 --- a/src/proguard/classfile/instruction/visitor/InstructionVisitor.java +++ b/core/src/proguard/classfile/instruction/visitor/InstructionVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java b/core/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java similarity index 80% rename from src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java rename to core/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java index a4be5409c..2727cae86 100644 --- a/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java +++ b/core/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,7 @@ import proguard.classfile.*; import proguard.classfile.attribute.CodeAttribute; import proguard.classfile.instruction.*; +import proguard.util.ArrayUtil; /** @@ -33,19 +34,17 @@ */ public class MultiInstructionVisitor implements InstructionVisitor { - private static final int ARRAY_SIZE_INCREMENT = 5; - - private InstructionVisitor[] instructionVisitors; private int instructionVisitorCount; public MultiInstructionVisitor() { + this.instructionVisitors = new InstructionVisitor[16]; } - public MultiInstructionVisitor(InstructionVisitor[] instructionVisitors) + public MultiInstructionVisitor(InstructionVisitor... instructionVisitors) { this.instructionVisitors = instructionVisitors; this.instructionVisitorCount = instructionVisitors.length; @@ -54,28 +53,10 @@ public MultiInstructionVisitor(InstructionVisitor[] instructionVisitors) 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; - } + instructionVisitors = + ArrayUtil.add(instructionVisitors, + instructionVisitorCount++, + instructionVisitor); } diff --git a/src/proguard/classfile/instruction/visitor/package.html b/core/src/proguard/classfile/instruction/visitor/package.html similarity index 100% rename from src/proguard/classfile/instruction/visitor/package.html rename to core/src/proguard/classfile/instruction/visitor/package.html diff --git a/src/proguard/classfile/io/LibraryClassReader.java b/core/src/proguard/classfile/io/LibraryClassReader.java similarity index 90% rename from src/proguard/classfile/io/LibraryClassReader.java rename to core/src/proguard/classfile/io/LibraryClassReader.java index f14d49838..253acaf77 100644 --- a/src/proguard/classfile/io/LibraryClassReader.java +++ b/core/src/proguard/classfile/io/LibraryClassReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -260,6 +260,15 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + char u2primitiveType = dataInput.readChar(); + int u4length = dataInput.readInt(); + + dataInput.skipBytes(primitiveSize(u2primitiveType) * u4length); + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { dataInput.skipBytes(2); @@ -313,6 +322,17 @@ public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTyp } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + dataInput.skipBytes(2); + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + dataInput.skipBytes(2); + } + // Small utility methods. /** @@ -357,6 +377,8 @@ private Constant createConstant() case ClassConstants.CONSTANT_Class: return new ClassConstant(); case ClassConstants.CONSTANT_MethodType: return new MethodTypeConstant(); case ClassConstants.CONSTANT_NameAndType: return new NameAndTypeConstant(); + case ClassConstants.CONSTANT_Module: return new ModuleConstant(); + case ClassConstants.CONSTANT_Package: return new PackageConstant(); default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool"); } @@ -380,4 +402,25 @@ private void skipAttribute() int u4attributeLength = dataInput.readInt(); dataInput.skipBytes(u4attributeLength); } + + + /** + * Returns the size in bytes of the given primitive type. + */ + private int primitiveSize(char primitiveType) + { + switch (primitiveType) + { + case ClassConstants.TYPE_BOOLEAN: + case ClassConstants.TYPE_BYTE: return 1; + case ClassConstants.TYPE_CHAR: + case ClassConstants.TYPE_SHORT: return 2; + case ClassConstants.TYPE_INT: + case ClassConstants.TYPE_FLOAT: return 4; + case ClassConstants.TYPE_LONG: + case ClassConstants.TYPE_DOUBLE: return 8; + } + + return 0; + } } diff --git a/src/proguard/classfile/io/ProgramClassReader.java b/core/src/proguard/classfile/io/ProgramClassReader.java similarity index 79% rename from src/proguard/classfile/io/ProgramClassReader.java rename to core/src/proguard/classfile/io/ProgramClassReader.java index 5a02a8c83..f6a31681d 100644 --- a/src/proguard/classfile/io/ProgramClassReader.java +++ b/core/src/proguard/classfile/io/ProgramClassReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,6 +26,8 @@ import proguard.classfile.attribute.annotation.target.*; import proguard.classfile.attribute.annotation.target.visitor.*; import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.module.*; +import proguard.classfile.attribute.module.visitor.*; import proguard.classfile.attribute.preverification.*; import proguard.classfile.attribute.preverification.visitor.*; import proguard.classfile.attribute.visitor.*; @@ -57,6 +59,10 @@ public class ProgramClassReader ParameterInfoVisitor, LocalVariableInfoVisitor, LocalVariableTypeInfoVisitor, + RequiresInfoVisitor, + ExportsInfoVisitor, + OpensInfoVisitor, + ProvidesInfoVisitor, AnnotationVisitor, TypeAnnotationVisitor, TargetInfoVisitor, @@ -72,7 +78,7 @@ public class ProgramClassReader */ public ProgramClassReader(DataInput dataInput) { - this.dataInput = new RuntimeDataInput(dataInput); + this.dataInput = new RuntimeDataInput(dataInput); } @@ -81,9 +87,9 @@ public ProgramClassReader(DataInput dataInput) public void visitProgramClass(ProgramClass programClass) { // Read and check the magic number. - programClass.u4magic = dataInput.readInt(); + int u4magic = dataInput.readInt(); - ClassUtil.checkMagicNumber(programClass.u4magic); + ClassUtil.checkMagicNumber(u4magic); // Read and check the version numbers. int u2minorVersion = dataInput.readUnsignedShort(); @@ -241,6 +247,109 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + char u2primitiveType = dataInput.readChar(); + int u4length = dataInput.readInt(); + + switch (u2primitiveType) + { + case ClassConstants.TYPE_BOOLEAN: + { + boolean[] values = new boolean[u4length]; + + for (int index = 0; index < u4length; index++) + { + values[index] = dataInput.readBoolean(); + } + + primitiveArrayConstant.values = values; + break; + } + case ClassConstants.TYPE_BYTE: + { + byte[] values = new byte[u4length]; + dataInput.readFully(values); + + primitiveArrayConstant.values = values; + break; + } + case ClassConstants.TYPE_CHAR: + { + char[] values = new char[u4length]; + + for (int index = 0; index < u4length; index++) + { + values[index] = dataInput.readChar(); + } + + primitiveArrayConstant.values = values; + break; + } + case ClassConstants.TYPE_SHORT: + { + short[] values = new short[u4length]; + + for (int index = 0; index < u4length; index++) + { + values[index] = dataInput.readShort(); + } + + primitiveArrayConstant.values = values; + break; + } + case ClassConstants.TYPE_INT: + { + int[] values = new int[u4length]; + + for (int index = 0; index < u4length; index++) + { + values[index] = dataInput.readInt(); + } + + primitiveArrayConstant.values = values; + break; + } + case ClassConstants.TYPE_FLOAT: + { + float[] values = new float[u4length]; + + for (int index = 0; index < u4length; index++) + { + values[index] = dataInput.readFloat(); + } + + primitiveArrayConstant.values = values; + break; + } + case ClassConstants.TYPE_LONG: + { + long[] values = new long[u4length]; + + for (int index = 0; index < u4length; index++) + { + values[index] = dataInput.readLong(); + } + + primitiveArrayConstant.values = values; + break; + } + case ClassConstants.TYPE_DOUBLE: + { + double[] values = new double[u4length]; + + for (int index = 0; index < u4length; index++) + { + values[index] = dataInput.readDouble(); + } + + primitiveArrayConstant.values = values; + break; + } + } + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { stringConstant.u2stringIndex = dataInput.readUnsignedShort(); @@ -272,6 +381,18 @@ public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHa } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + moduleConstant.u2nameIndex = dataInput.readUnsignedShort(); + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + packageConstant.u2nameIndex = dataInput.readUnsignedShort(); + } + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) { refConstant.u2classIndex = dataInput.readUnsignedShort(); @@ -358,6 +479,85 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + moduleAttribute.u2moduleNameIndex = dataInput.readUnsignedShort(); + moduleAttribute.u2moduleFlags = dataInput.readUnsignedShort(); + moduleAttribute.u2moduleVersionIndex = dataInput.readUnsignedShort(); + + // Read the requires. + moduleAttribute.u2requiresCount = dataInput.readUnsignedShort(); + + moduleAttribute.requires = new RequiresInfo[moduleAttribute.u2requiresCount]; + for (int index = 0; index < moduleAttribute.u2requiresCount; index++) + { + RequiresInfo requiresInfo = new RequiresInfo(); + visitRequiresInfo(clazz, requiresInfo); + moduleAttribute.requires[index] = requiresInfo; + } + + // Read the exports. + moduleAttribute.u2exportsCount = dataInput.readUnsignedShort(); + + moduleAttribute.exports = new ExportsInfo[moduleAttribute.u2exportsCount]; + for (int index = 0; index < moduleAttribute.u2exportsCount; index++) + { + ExportsInfo exportsInfo = new ExportsInfo(); + visitExportsInfo(clazz, exportsInfo); + moduleAttribute.exports[index] = exportsInfo; + } + + // Read the opens. + moduleAttribute.u2opensCount = dataInput.readUnsignedShort(); + + moduleAttribute.opens = new OpensInfo[moduleAttribute.u2opensCount]; + for (int index = 0; index < moduleAttribute.u2opensCount; index++) + { + OpensInfo opensInfo = new OpensInfo(); + visitOpensInfo(clazz, opensInfo); + moduleAttribute.opens[index] = opensInfo; + } + + // Read the uses. + moduleAttribute.u2usesCount = dataInput.readUnsignedShort(); + + moduleAttribute.u2uses = new int[moduleAttribute.u2usesCount]; + for (int index = 0; index < moduleAttribute.u2usesCount; index++) + { + moduleAttribute.u2uses[index] = dataInput.readUnsignedShort(); + } + + // Read the provides. + moduleAttribute.u2providesCount = dataInput.readUnsignedShort(); + + moduleAttribute.provides = new ProvidesInfo[moduleAttribute.u2providesCount]; + for (int index = 0; index < moduleAttribute.u2providesCount; index++) + { + ProvidesInfo providesInfo = new ProvidesInfo(); + visitProvidesInfo(clazz, providesInfo); + moduleAttribute.provides[index] = providesInfo; + } + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + moduleMainClassAttribute.u2mainClass = dataInput.readUnsignedShort(); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + // Read the packages. + modulePackagesAttribute.u2packagesCount = dataInput.readUnsignedShort(); + + modulePackagesAttribute.u2packages = new int[modulePackagesAttribute.u2packagesCount]; + for (int index = 0; index < modulePackagesAttribute.u2packagesCount; index++) { + modulePackagesAttribute.u2packages[index] = dataInput.readUnsignedShort(); + } + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { // This attribute does not contain any additional information. @@ -772,6 +972,69 @@ public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute } + // Implementations for RequiresInfoVisitor. + + public void visitRequiresInfo(Clazz clazz, RequiresInfo requiresInfo) + { + requiresInfo.u2requiresIndex = dataInput.readUnsignedShort(); + requiresInfo.u2requiresFlags = dataInput.readUnsignedShort(); + requiresInfo.u2requiresVersionIndex = dataInput.readUnsignedShort(); + } + + + // Implementations for ExportsInfoVisitor. + + public void visitExportsInfo(Clazz clazz, ExportsInfo exportsInfo) + { + exportsInfo.u2exportsIndex = dataInput.readUnsignedShort(); + exportsInfo.u2exportsFlags = dataInput.readUnsignedShort(); + + // Read the targets. + exportsInfo.u2exportsToCount = dataInput.readUnsignedShort(); + + exportsInfo.u2exportsToIndex = new int[exportsInfo.u2exportsToCount]; + for (int index = 0; index < exportsInfo.u2exportsToCount; index++) + { + exportsInfo.u2exportsToIndex[index] = dataInput.readUnsignedShort(); + } + } + + + // Implementations for OpensInfoVisitor. + + public void visitOpensInfo(Clazz clazz, OpensInfo opensInfo) + { + opensInfo.u2opensIndex = dataInput.readUnsignedShort(); + opensInfo.u2opensFlags = dataInput.readUnsignedShort(); + + // Read the targets. + opensInfo.u2opensToCount = dataInput.readUnsignedShort(); + + opensInfo.u2opensToIndex = new int[opensInfo.u2opensToCount]; + for (int index = 0; index < opensInfo.u2opensToCount; index++) + { + opensInfo.u2opensToIndex[index] = dataInput.readUnsignedShort(); + } + } + + + // Implementations for ProvidesInfoVisitor. + + public void visitProvidesInfo(Clazz clazz, ProvidesInfo providesInfo) + { + providesInfo.u2providesIndex = dataInput.readUnsignedShort(); + + // Read the withs. + providesInfo.u2providesWithCount = dataInput.readUnsignedShort(); + + providesInfo.u2providesWithIndex = new int[providesInfo.u2providesWithCount]; + for (int index = 0; index < providesInfo.u2providesWithCount; index++) + { + providesInfo.u2providesWithIndex[index] = dataInput.readUnsignedShort(); + } + } + + // Implementations for AnnotationVisitor. public void visitAnnotation(Clazz clazz, Annotation annotation) @@ -967,6 +1230,7 @@ private Constant createConstant() case ClassConstants.CONSTANT_Float: return new FloatConstant(); case ClassConstants.CONSTANT_Long: return new LongConstant(); case ClassConstants.CONSTANT_Double: return new DoubleConstant(); + case ClassConstants.CONSTANT_PrimitiveArray: return new PrimitiveArrayConstant(); case ClassConstants.CONSTANT_String: return new StringConstant(); case ClassConstants.CONSTANT_Utf8: return new Utf8Constant(); case ClassConstants.CONSTANT_InvokeDynamic: return new InvokeDynamicConstant(); @@ -977,6 +1241,8 @@ private Constant createConstant() case ClassConstants.CONSTANT_Class: return new ClassConstant(); case ClassConstants.CONSTANT_MethodType: return new MethodTypeConstant(); case ClassConstants.CONSTANT_NameAndType: return new NameAndTypeConstant(); + case ClassConstants.CONSTANT_Module: return new ModuleConstant(); + case ClassConstants.CONSTANT_Package: return new PackageConstant(); default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool"); } @@ -990,31 +1256,34 @@ private Attribute createAttribute(Clazz clazz) 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_MethodParameters) ? (Attribute)new MethodParametersAttribute(): - 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_RuntimeVisibleTypeAnnotations) ? (Attribute)new RuntimeVisibleTypeAnnotationsAttribute(): - attributeName.equals(ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations) ? (Attribute)new RuntimeInvisibleTypeAnnotationsAttribute(): - attributeName.equals(ClassConstants.ATTR_AnnotationDefault) ? (Attribute)new AnnotationDefaultAttribute(): - (Attribute)new UnknownAttribute(u2attributeNameIndex, u4attributeLength); + 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_MethodParameters) ? (Attribute)new MethodParametersAttribute(): + 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_RuntimeVisibleTypeAnnotations) ? (Attribute)new RuntimeVisibleTypeAnnotationsAttribute(): + attributeName.equals(ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations) ? (Attribute)new RuntimeInvisibleTypeAnnotationsAttribute(): + attributeName.equals(ClassConstants.ATTR_AnnotationDefault) ? (Attribute)new AnnotationDefaultAttribute(): + attributeName.equals(ClassConstants.ATTR_Module) ? (Attribute)new ModuleAttribute(): + attributeName.equals(ClassConstants.ATTR_ModuleMainClass) ? (Attribute)new ModuleMainClassAttribute(): + attributeName.equals(ClassConstants.ATTR_ModulePackages) ? (Attribute)new ModulePackagesAttribute(): + (Attribute)new UnknownAttribute(u2attributeNameIndex, u4attributeLength); attribute.u2attributeNameIndex = u2attributeNameIndex; return attribute; diff --git a/src/proguard/classfile/io/ProgramClassWriter.java b/core/src/proguard/classfile/io/ProgramClassWriter.java similarity index 81% rename from src/proguard/classfile/io/ProgramClassWriter.java rename to core/src/proguard/classfile/io/ProgramClassWriter.java index 13fe74c68..6f24b1021 100644 --- a/src/proguard/classfile/io/ProgramClassWriter.java +++ b/core/src/proguard/classfile/io/ProgramClassWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,11 +26,13 @@ import proguard.classfile.attribute.annotation.target.*; import proguard.classfile.attribute.annotation.target.visitor.*; import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.module.*; +import proguard.classfile.attribute.module.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.constant.visitor.*; import proguard.classfile.util.*; import proguard.classfile.visitor.*; @@ -72,7 +74,7 @@ public ProgramClassWriter(DataOutput dataOutput) public void visitProgramClass(ProgramClass programClass) { // Write the magic number. - dataOutput.writeInt(programClass.u4magic); + dataOutput.writeInt(ClassConstants.MAGIC); // Write the version numbers. dataOutput.writeShort(ClassUtil.internalMinorClassVersion(programClass.u4version)); @@ -84,7 +86,9 @@ public void visitProgramClass(ProgramClass programClass) programClass.constantPoolEntriesAccept(this); // Write the general class information. - dataOutput.writeUnsignedShort(programClass.u2accessFlags); + // Ignore the higher bits outside the short range - these are for + // internal purposes only. + dataOutput.writeUnsignedShort(programClass.u2accessFlags & 0xffff); dataOutput.writeUnsignedShort(programClass.u2thisClass); dataOutput.writeUnsignedShort(programClass.u2superClass); @@ -123,7 +127,9 @@ public void visitLibraryClass(LibraryClass libraryClass) public void visitProgramField(ProgramClass programClass, ProgramField programField) { // Write the general field information. - dataOutput.writeUnsignedShort(programField.u2accessFlags); + // Ignore the higher bits outside the short range - these are for + // internal purposes only. + dataOutput.writeUnsignedShort(programField.u2accessFlags & 0xffff); dataOutput.writeUnsignedShort(programField.u2nameIndex); dataOutput.writeUnsignedShort(programField.u2descriptorIndex); @@ -137,7 +143,9 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { // Write the general method information. - dataOutput.writeUnsignedShort(programMethod.u2accessFlags); + // Ignore the higher bits outside the short range - these are for + // internal purposes only. + dataOutput.writeUnsignedShort(programMethod.u2accessFlags & 0xffff); dataOutput.writeUnsignedShort(programMethod.u2nameIndex); dataOutput.writeUnsignedShort(programMethod.u2descriptorIndex); @@ -167,7 +175,8 @@ public void visitAnyConstant(Clazz clazz, Constant constant) private class ConstantBodyWriter extends SimplifiedVisitor - implements ConstantVisitor + implements ConstantVisitor, + PrimitiveArrayConstantElementVisitor { // Implementations for ConstantVisitor. @@ -195,6 +204,19 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + char u2primitiveType = primitiveArrayConstant.getPrimitiveType(); + int u4Length = primitiveArrayConstant.getLength(); + + dataOutput.writeUnsignedShort(u2primitiveType); + dataOutput.writeInt(u4Length); + + // Write the array values. + primitiveArrayConstant.primitiveArrayElementsAccept(clazz, this); + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { dataOutput.writeUnsignedShort(stringConstant.u2stringIndex); @@ -248,6 +270,68 @@ public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTyp dataOutput.writeUnsignedShort(nameAndTypeConstant.u2nameIndex); dataOutput.writeUnsignedShort(nameAndTypeConstant.u2descriptorIndex); } + + + // Implementations for PrimitiveArrayConstantElementVisitor. + + public void visitBooleanArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, boolean value) + { + dataOutput.writeBoolean(value); + } + + + public void visitByteArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, byte value) + { + dataOutput.writeByte(value); + } + + + public void visitCharArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, char value) + { + dataOutput.writeChar(value); + } + + + public void visitShortArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, short value) + { + dataOutput.writeShort(value); + } + + + public void visitIntArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, int value) + { + dataOutput.writeInt(value); + } + + + public void visitFloatArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, float value) + { + dataOutput.writeFloat(value); + } + + + public void visitLongArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, long value) + { + dataOutput.writeLong(value); + } + + + public void visitDoubleArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, double value) + { + dataOutput.writeDouble(value); + } + + + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + dataOutput.writeUnsignedShort(moduleConstant.u2nameIndex); + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + dataOutput.writeUnsignedShort(packageConstant.u2nameIndex); + } } @@ -294,6 +378,10 @@ private class AttributeBodyWriter ParameterInfoVisitor, LocalVariableInfoVisitor, LocalVariableTypeInfoVisitor, + RequiresInfoVisitor, + ExportsInfoVisitor, + OpensInfoVisitor, + ProvidesInfoVisitor, AnnotationVisitor, TypeAnnotationVisitor, TargetInfoVisitor, @@ -347,6 +435,60 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + dataOutput.writeUnsignedShort(moduleAttribute.u2moduleNameIndex); + dataOutput.writeUnsignedShort(moduleAttribute.u2moduleFlags); + dataOutput.writeUnsignedShort(moduleAttribute.u2moduleVersionIndex); + + // Write the requires. + dataOutput.writeUnsignedShort(moduleAttribute.u2requiresCount); + + moduleAttribute.requiresAccept(clazz, this); + + // Write the exports. + dataOutput.writeUnsignedShort(moduleAttribute.u2exportsCount); + + moduleAttribute.exportsAccept(clazz, this); + + // Write the opens. + dataOutput.writeUnsignedShort(moduleAttribute.u2opensCount); + + moduleAttribute.opensAccept(clazz, this); + + // Write the uses. + dataOutput.writeUnsignedShort(moduleAttribute.u2usesCount); + + for (int index = 0; index < moduleAttribute.u2usesCount; index++) + { + dataOutput.writeUnsignedShort(moduleAttribute.u2uses[index]); + } + + // Write the provides. + dataOutput.writeUnsignedShort(moduleAttribute.u2providesCount); + + moduleAttribute.providesAccept(clazz, this); + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + dataOutput.writeUnsignedShort(moduleMainClassAttribute.u2mainClass); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + // Write the packages. + dataOutput.writeUnsignedShort(modulePackagesAttribute.u2packagesCount); + + for (int index = 0; index < modulePackagesAttribute.u2packagesCount; index++) + { + dataOutput.writeUnsignedShort(modulePackagesAttribute.u2packages[index]); + } + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { // This attribute does not contain any additional information. @@ -460,6 +602,58 @@ public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, Cod } + public void visitRequiresInfo(Clazz clazz, RequiresInfo requiresInfo) + { + dataOutput.writeUnsignedShort(requiresInfo.u2requiresIndex); + dataOutput.writeUnsignedShort(requiresInfo.u2requiresFlags); + dataOutput.writeUnsignedShort(requiresInfo.u2requiresVersionIndex); + } + + + public void visitExportsInfo(Clazz clazz, ExportsInfo exportsInfo) + { + dataOutput.writeUnsignedShort(exportsInfo.u2exportsIndex); + dataOutput.writeUnsignedShort(exportsInfo.u2exportsFlags); + + // Write tthe argets. + dataOutput.writeUnsignedShort(exportsInfo.u2exportsToCount); + + for (int index = 0; index < exportsInfo.u2exportsToCount; index++) + { + dataOutput.writeUnsignedShort(exportsInfo.u2exportsToIndex[index]); + } + } + + + public void visitOpensInfo(Clazz clazz, OpensInfo opensInfo) + { + dataOutput.writeUnsignedShort(opensInfo.u2opensIndex); + dataOutput.writeUnsignedShort(opensInfo.u2opensFlags); + + // Write the targets. + dataOutput.writeUnsignedShort(opensInfo.u2opensToCount); + + for (int index = 0; index < opensInfo.u2opensToCount; index++) + { + dataOutput.writeUnsignedShort(opensInfo.u2opensToIndex[index]); + } + } + + + public void visitProvidesInfo(Clazz clazz, ProvidesInfo providesInfo) + { + dataOutput.writeUnsignedShort(providesInfo.u2providesIndex); + + // Write the with. + dataOutput.writeUnsignedShort(providesInfo.u2providesWithCount); + + for (int index = 0; index < providesInfo.u2providesWithCount; index++) + { + dataOutput.writeUnsignedShort(providesInfo.u2providesWithIndex[index]); + } + } + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) { // Write the annotations. @@ -561,8 +755,10 @@ public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute code public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) { - dataOutput.writeUnsignedShort(lineNumberInfo.u2startPC); - dataOutput.writeUnsignedShort(lineNumberInfo.u2lineNumber); + // Simply suppress line number overflows, typically caused by + // inlined methods and their artificial line numbers. + dataOutput.writeUnsignedShort(lineNumberInfo.u2startPC > 0xffff ? 0 : lineNumberInfo.u2startPC); + dataOutput.writeUnsignedShort(lineNumberInfo.u2lineNumber > 0xffff ? 0 : lineNumberInfo.u2lineNumber); } diff --git a/src/proguard/classfile/io/RuntimeDataInput.java b/core/src/proguard/classfile/io/RuntimeDataInput.java similarity index 98% rename from src/proguard/classfile/io/RuntimeDataInput.java rename to core/src/proguard/classfile/io/RuntimeDataInput.java index 5796269c2..e496ed49d 100644 --- a/src/proguard/classfile/io/RuntimeDataInput.java +++ b/core/src/proguard/classfile/io/RuntimeDataInput.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/io/RuntimeDataOutput.java b/core/src/proguard/classfile/io/RuntimeDataOutput.java similarity index 98% rename from src/proguard/classfile/io/RuntimeDataOutput.java rename to core/src/proguard/classfile/io/RuntimeDataOutput.java index 781c6c9e5..b56fd9c3c 100644 --- a/src/proguard/classfile/io/RuntimeDataOutput.java +++ b/core/src/proguard/classfile/io/RuntimeDataOutput.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/io/package.html b/core/src/proguard/classfile/io/package.html similarity index 100% rename from src/proguard/classfile/io/package.html rename to core/src/proguard/classfile/io/package.html diff --git a/src/proguard/classfile/package.html b/core/src/proguard/classfile/package.html similarity index 100% rename from src/proguard/classfile/package.html rename to core/src/proguard/classfile/package.html diff --git a/src/proguard/classfile/util/AccessUtil.java b/core/src/proguard/classfile/util/AccessUtil.java similarity index 98% rename from src/proguard/classfile/util/AccessUtil.java rename to core/src/proguard/classfile/util/AccessUtil.java index 6799fa7fb..593907db8 100644 --- a/src/proguard/classfile/util/AccessUtil.java +++ b/core/src/proguard/classfile/util/AccessUtil.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/AllParameterVisitor.java b/core/src/proguard/classfile/util/AllParameterVisitor.java similarity index 85% rename from src/proguard/classfile/util/AllParameterVisitor.java rename to core/src/proguard/classfile/util/AllParameterVisitor.java index b665d2b73..96211daf0 100644 --- a/src/proguard/classfile/util/AllParameterVisitor.java +++ b/core/src/proguard/classfile/util/AllParameterVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,14 +25,15 @@ /** * This MemberVisitor lets a given parameter visitor visit all the parameters - * of the methods that it visits. The parameters do not include or count the - * 'this' parameter or the return value. + * of the methods that it visits. The parameters optionally includes the + * 'this' parameter of non-static methods, but never the return value. * * @author Eric Lafortune */ public class AllParameterVisitor implements MemberVisitor { + private final boolean includeThisParameter; private final ParameterVisitor parameterVisitor; @@ -40,9 +41,11 @@ public class AllParameterVisitor * Creates a new AllParameterVisitor for the given parameter * visitor. */ - public AllParameterVisitor(ParameterVisitor parameterVisitor) + public AllParameterVisitor(boolean includeThisParameter, + ParameterVisitor parameterVisitor) { - this.parameterVisitor = parameterVisitor; + this.includeThisParameter = includeThisParameter; + this.parameterVisitor = parameterVisitor; } @@ -111,8 +114,8 @@ private void visitParameters(Clazz clazz, String descriptor = method.getDescriptor(clazz); // Count the number of parameters and their total size. - int parameterCount = 0; - int parameterSize = 0; + int parameterCount = 0; + int parameterSize = 0; int index = 1; @@ -164,10 +167,24 @@ private void visitParameters(Clazz clazz, } // Visit the parameters. - int parameterIndex = 0; - int parameterOffset = 0; + int parameterIndex = 0; + int parameterOffset = 0; int referenceClassIndex = 0; + // Visit the 'this' parameter if applicable. + if (includeThisParameter && + (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0) + { + parameterVisitor.visitParameter(clazz, + method, + parameterIndex++, + ++parameterCount, + parameterOffset++, + ++parameterSize, + ClassUtil.internalTypeFromClassName(clazz.getName()), + clazz); + } + index = 1; while (true) diff --git a/core/src/proguard/classfile/util/ArrayInitializationMatcher.java b/core/src/proguard/classfile/util/ArrayInitializationMatcher.java new file mode 100644 index 000000000..3f6257a1f --- /dev/null +++ b/core/src/proguard/classfile/util/ArrayInitializationMatcher.java @@ -0,0 +1,347 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.Constant; +import proguard.classfile.instruction.*; +import proguard.evaluation.TracedStack; +import proguard.evaluation.value.*; +import proguard.optimize.evaluation.PartialEvaluator; +import proguard.optimize.peephole.InstructionSequenceReplacer; + +/** + * This class finds sequences of instructions that correspond to primitive + * array initializations. Such initializations may be represented more + * efficiently in other bytecode languages. + * + * @author Eric Lafortune + */ +public class ArrayInitializationMatcher +{ + private static final int X = InstructionSequenceReplacer.X; + + private final PartialEvaluator partialEvaluator; + + private int arrayInitializationStart; + private int arrayInitializationEnd; + private Object array; + + private final Constant[] CONSTANTS = new Constant[0]; + + // Conversion with dex2jar might result in arrays + // being pre-stored to a variable before initialization: + // + // newarray + // astore X + // aload X + // initialization start + // + private final Instruction[] ARRAY_PRESTORE_INSTRUCTIONS = new Instruction[] + { + new VariableInstruction(InstructionConstants.OP_ASTORE, X), + new VariableInstruction(InstructionConstants.OP_ALOAD, X) + }; + + private final InstructionSequenceMatcher arrayPreStoreMatcher = + new InstructionSequenceMatcher(CONSTANTS, ARRAY_PRESTORE_INSTRUCTIONS); + + + /** + * Creates a new ArrayInitializationMatcher. + */ + public ArrayInitializationMatcher() + { + this(new PartialEvaluator()); + } + + + /** + * Creates a new ArrayInitializationMatcher that will use the given partial + * evaluator. The evaluator must have been initialized before trying to + * match any array initializations. + * @param partialEvaluator the evaluator to be used for the analysis. + */ + public ArrayInitializationMatcher(PartialEvaluator partialEvaluator) + { + this.partialEvaluator = partialEvaluator; + } + + + /** + * Returns whether the code fragment starting at the specified newarray + * instruction is followed by a static array initialization. + * @param clazz the class. + * @param method the method. + * @param codeAttribute the code attribute. + * @param newArrayOffset the offset of the newarray instruction. + * @param newArrayInstruction the newarray instruction. + * @return whether there is a static array initialization. + */ + public boolean matchesArrayInitialization(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int newArrayOffset, + SimpleInstruction newArrayInstruction) + { + array = null; + + TracedStack stackBefore = + partialEvaluator.getStackBefore(newArrayOffset); + + IntegerValue integerValue = + stackBefore.getTop(0).integerValue(); + + if (!integerValue.isParticular()) + { + return false; + } + + + int arrayLength = integerValue.value(); + + int newArrayType = newArrayInstruction.constant; + int arrayStoreOpcode = arrayStoreOpcode(newArrayType); + + byte[] code = codeAttribute.code; + + int offset = newArrayOffset; + Instruction instruction = newArrayInstruction; + + int skipOffset = skipPreStoreInstructions(clazz, method, codeAttribute, offset + instruction.length(offset)); + if (skipOffset > 0) + { + offset = skipOffset; + newArrayOffset = offset; + instruction = InstructionFactory.create(code, offset); + } + + // Remember the potential initialization start. + int tmpInitializationStart = offset + instruction.length(offset); + + // Check if all the elements in the array are initialized. + for (int index = 0; index < arrayLength; index++) + { + // Check if the array reference is pushed. + instruction = InstructionFactory.create(code, offset += instruction.length(offset)); + + if (instruction.stackPushCount(clazz) < 1 || + !partialEvaluator.getStackAfter(offset).getTopActualProducerValue(0).instructionOffsetValue().contains(newArrayOffset)) + { + return false; + } + + // Check that the array index is pushed. + instruction = InstructionFactory.create(code, offset += instruction.length(offset)); + if (instruction.stackPushCount(clazz) != 1) + { + return false; + } + + Value indexValue = partialEvaluator.getStackAfter(offset).getTop(0); + if (indexValue.computationalType() != Value.TYPE_INTEGER || + !indexValue.integerValue().isParticular() || + indexValue.integerValue().value() != index) + { + return false; + } + + // Check if a particular value is pushed. + instruction = InstructionFactory.create(code, offset += instruction.length(offset)); + if (instruction.stackPushCount(clazz) < 1 || + !partialEvaluator.getStackAfter(offset).getTop(0).isParticular()) + { + return false; + } + + Value elementValue = partialEvaluator.getStackAfter(offset).getTop(0); + + // Check if the value is stored in the array. + instruction = InstructionFactory.create(code, offset += instruction.length(offset)); + if (instruction.opcode != arrayStoreOpcode) + { + return false; + } + + if (index == 0) + { + array = newArray(newArrayType, arrayLength); + } + + arrayStore(newArrayType, array, index, elementValue); + } + + arrayInitializationStart = tmpInitializationStart; + arrayInitializationEnd = offset; + + return offset > newArrayOffset; + } + + + /** + * Returns the offset to skip to after a new-array instruction. + *

+ * This is a work-around for code converted by dex2jar. In some + * cases, after an array has been created, it is immediately + * stored into a variable and loaded again: + *

+     *   newarray
+     *   astore X
+     *   aload  X
+     *   initialization
+     * 
+ * + * @param clazz the class. + * @param method the method. + * @param codeAttribute the code attribute. + * @param startOffset the start offset. + * @return the offset to skip to + */ + private int skipPreStoreInstructions(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int startOffset) + { + final int instructionCount = arrayPreStoreMatcher.instructionCount(); + + arrayPreStoreMatcher.reset(); + for (int count = 0, offset = startOffset; + count < instructionCount && offset < codeAttribute.u4codeLength; + count++) + { + Instruction instruction = + InstructionFactory.create(codeAttribute.code, offset); + + instruction.accept(clazz, method, codeAttribute, offset, + arrayPreStoreMatcher); + + offset += instruction.length(offset); + } + + if (arrayPreStoreMatcher.isMatching()) + { + return arrayPreStoreMatcher.matchedInstructionOffset(instructionCount - 1); + } + + return -1; + } + + /** + * Returns the first offset of the recent static array initialization, i.e. the first + * initialization instruction after the newarray. + * @see #matchesArrayInitialization + */ + public int arrayInitializationStart() + { + return arrayInitializationStart; + } + + /** + * Returns the last offset of the recent static array initialization. + * @see #matchesArrayInitialization + */ + public int arrayInitializationEnd() + { + return arrayInitializationEnd; + } + + /** + * Returns the recent static array initialization. + * @see #matchesArrayInitialization + */ + public Object array() + { + return array; + } + + + private byte internalType(int newArrayType) + { + switch (newArrayType) + { + case InstructionConstants.ARRAY_T_BOOLEAN: + case InstructionConstants.ARRAY_T_BYTE: + case InstructionConstants.ARRAY_T_CHAR: + case InstructionConstants.ARRAY_T_SHORT: + case InstructionConstants.ARRAY_T_INT: return Value.TYPE_INTEGER; + case InstructionConstants.ARRAY_T_LONG: return Value.TYPE_LONG; + case InstructionConstants.ARRAY_T_FLOAT: return Value.TYPE_FLOAT; + case InstructionConstants.ARRAY_T_DOUBLE: return Value.TYPE_DOUBLE; + default: + throw new IllegalArgumentException("Unexpected new array type ["+newArrayType+"]"); + } + } + + + private byte arrayStoreOpcode(int newArrayType) + { + switch (newArrayType) + { + case InstructionConstants.ARRAY_T_BOOLEAN: + case InstructionConstants.ARRAY_T_BYTE: return InstructionConstants.OP_BASTORE; + case InstructionConstants.ARRAY_T_CHAR: return InstructionConstants.OP_CASTORE; + case InstructionConstants.ARRAY_T_SHORT: return InstructionConstants.OP_SASTORE; + case InstructionConstants.ARRAY_T_INT: return InstructionConstants.OP_IASTORE; + case InstructionConstants.ARRAY_T_LONG: return InstructionConstants.OP_LASTORE; + case InstructionConstants.ARRAY_T_FLOAT: return InstructionConstants.OP_FASTORE; + case InstructionConstants.ARRAY_T_DOUBLE: return InstructionConstants.OP_DASTORE; + default: + throw new IllegalArgumentException("Unexpected new array type ["+newArrayType+"]"); + } + } + + + private Object newArray(int newArrayType, int arrayLength) + { + switch (newArrayType) + { + case InstructionConstants.ARRAY_T_BOOLEAN: return new boolean[arrayLength]; + case InstructionConstants.ARRAY_T_BYTE: return new byte[arrayLength]; + case InstructionConstants.ARRAY_T_CHAR: return new char[arrayLength]; + case InstructionConstants.ARRAY_T_SHORT: return new short[arrayLength]; + case InstructionConstants.ARRAY_T_INT: return new int[arrayLength]; + case InstructionConstants.ARRAY_T_LONG: return new long[arrayLength]; + case InstructionConstants.ARRAY_T_FLOAT: return new float[arrayLength]; + case InstructionConstants.ARRAY_T_DOUBLE: return new double[arrayLength]; + default: + throw new IllegalArgumentException("Unexpected new array type ["+newArrayType+"]"); + } + } + + + private void arrayStore(int newArrayType, Object array, int index, Value value) + { + switch (newArrayType) + { + case InstructionConstants.ARRAY_T_BOOLEAN: ((boolean[])array)[index] = 0 != value.integerValue().value(); break; + case InstructionConstants.ARRAY_T_BYTE: ((byte [])array)[index] = (byte) value.integerValue().value(); break; + case InstructionConstants.ARRAY_T_CHAR: ((char [])array)[index] = (char) value.integerValue().value(); break; + case InstructionConstants.ARRAY_T_SHORT: ((short [])array)[index] = (short)value.integerValue().value(); break; + case InstructionConstants.ARRAY_T_INT: ((int [])array)[index] = value.integerValue().value(); break; + case InstructionConstants.ARRAY_T_LONG: ((long [])array)[index] = value.longValue().value(); break; + case InstructionConstants.ARRAY_T_FLOAT: ((float [])array)[index] = value.floatValue().value(); break; + case InstructionConstants.ARRAY_T_DOUBLE: ((double [])array)[index] = value.doubleValue().value(); break; + default: + throw new IllegalArgumentException("Unexpected new array type ["+newArrayType+"]"); + } + } +} \ No newline at end of file diff --git a/core/src/proguard/classfile/util/ArrayInitializationReplacer.java b/core/src/proguard/classfile/util/ArrayInitializationReplacer.java new file mode 100644 index 000000000..2b273ed23 --- /dev/null +++ b/core/src/proguard/classfile/util/ArrayInitializationReplacer.java @@ -0,0 +1,200 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.editor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.visitor.ClassVisitor; +import proguard.evaluation.BasicInvocationUnit; +import proguard.evaluation.value.*; +import proguard.optimize.evaluation.PartialEvaluator; + +/** + * This ClassVisitor replaces array initialization instructions with optimized + * primitive array constants. + * + * These constants are not supported by any Java specification and therefore + * only for internal use. + * + * @see PrimitiveArrayConstantReplacer + * @author Thomas Neidhart + */ +public class ArrayInitializationReplacer +extends SimplifiedVisitor +implements ClassVisitor, + + // Implementation interfaces. + AttributeVisitor, + InstructionVisitor +{ + private final ValueFactory valueFactory = new ParticularValueFactory(new BasicValueFactory()); + private final PartialEvaluator partialEvaluator = new PartialEvaluator(valueFactory, + new BasicInvocationUnit(valueFactory), + true); + private final ArrayInitializationMatcher arrayInitializationMatcher = new ArrayInitializationMatcher(partialEvaluator); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + private ConstantPoolEditor constantPoolEditor; + private int lastInstructionOffset; + private int lastInstructionStackPushCount; + private int arrayInitializationStart; + private int arrayInitializationEnd; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + constantPoolEditor = new ConstantPoolEditor(programClass); + + // Visit all methods that have "NEWARRAY" instructions. + programClass.methodsAccept( + new AllAttributeVisitor( + new ArrayInitializationFilter( + this))); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + lastInstructionOffset = -1; + lastInstructionStackPushCount = -1; + arrayInitializationStart = -1; + arrayInitializationEnd = -1; + codeAttribute.instructionsAccept(clazz, method, this); + + if (codeAttributeEditor.isModified()) + { + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction) + { + // Verify that the previous instruction pushed the array size on the + // stack: the java compiler will always do so, but obfuscators may + // have reordered the instructions. + if (instruction.opcode == InstructionConstants.OP_NEWARRAY && + lastInstructionStackPushCount == 1) + { + if (arrayInitializationMatcher.matchesArrayInitialization(clazz, + method, + codeAttribute, + offset, + (SimpleInstruction)instruction)) + { + Object values = arrayInitializationMatcher.array(); + int constantIndex = constantPoolEditor.addPrimitiveArrayConstant(values); + + // We need to replace the previous instruction, which pushes + // the array length onto the stack. + codeAttributeEditor.replaceInstruction(lastInstructionOffset, + new ConstantInstruction(InstructionConstants.OP_LDC, + constantIndex)); + + // Remove the newarray instruction itself. + codeAttributeEditor.deleteInstruction(offset); + + // Mark the start/end of the array initialization sequence. + arrayInitializationStart = arrayInitializationMatcher.arrayInitializationStart(); + arrayInitializationEnd = arrayInitializationMatcher.arrayInitializationEnd(); + } + } + + // Remove any instruction inside the array initialization sequence. + if (arrayInitializationEnd != -1 && + offset >= arrayInitializationStart && + offset <= arrayInitializationEnd) + { + codeAttributeEditor.deleteInstruction(offset); + } + + lastInstructionOffset = offset; + lastInstructionStackPushCount = instruction.stackPushCount(clazz); + } + + + /** + * Private utility class to visit only CodeAttributes that contain + * "NEWARRAY" instructions. + */ + private static class ArrayInitializationFilter + extends SimplifiedVisitor + implements AttributeVisitor + { + private final AttributeVisitor acceptedVisitor; + + + public ArrayInitializationFilter(AttributeVisitor acceptedVisitor) + { + this.acceptedVisitor = acceptedVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + boolean delegateVisit = false; + // Directly iterate of all instructions and exit early if + // we encounter a "NEWARRAY" instruction. + for (int offset = 0; offset < codeAttribute.u4codeLength;) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, offset); + if (instruction.opcode == InstructionConstants.OP_NEWARRAY) + { + delegateVisit = true; + break; + } + + offset += instruction.length(offset); + } + + if (delegateVisit) + { + acceptedVisitor.visitCodeAttribute(clazz, method, codeAttribute); + } + } + } +} diff --git a/src/proguard/classfile/util/ClassReferenceInitializer.java b/core/src/proguard/classfile/util/ClassReferenceInitializer.java similarity index 92% rename from src/proguard/classfile/util/ClassReferenceInitializer.java rename to core/src/proguard/classfile/util/ClassReferenceInitializer.java index 5a6bf8d81..beb14a827 100644 --- a/src/proguard/classfile/util/ClassReferenceInitializer.java +++ b/core/src/proguard/classfile/util/ClassReferenceInitializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -71,6 +71,17 @@ public class ClassReferenceInitializer private final MemberFinder memberFinder = new MemberFinder(); + /** + * Creates a new ClassReferenceInitializer that initializes the references + * of all visited class files. + */ + public ClassReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool) + { + this(programClassPool, libraryClassPool, null, null, null, null); + } + + /** * Creates a new ClassReferenceInitializer that initializes the references * of all visited class files, optionally printing warnings if some classes @@ -225,20 +236,23 @@ public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) missingProgramMemberWarningPrinter : missingLibraryMemberWarningPrinter; - 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 " + - (isProgramClass ? - "program" : - "library") + - " class " + - ClassUtil.externalClassName(className)); + if (missingMemberWarningPrinter != null) + { + 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 " + + (isProgramClass ? + "program" : + "library") + + " class " + + ClassUtil.externalClassName(className)); + } } } } @@ -294,7 +308,8 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute.referencedMethod = enclosingMethodAttribute.referencedClass.findMethod(name, type); - if (enclosingMethodAttribute.referencedMethod == null) + if (enclosingMethodAttribute.referencedMethod == null && + missingProgramMemberWarningPrinter != null) { // We couldn't find the enclosing method. String className = clazz.getName(); diff --git a/src/proguard/classfile/util/ClassSubHierarchyInitializer.java b/core/src/proguard/classfile/util/ClassSubHierarchyInitializer.java similarity index 97% rename from src/proguard/classfile/util/ClassSubHierarchyInitializer.java rename to core/src/proguard/classfile/util/ClassSubHierarchyInitializer.java index ef5df7e9f..991ac838a 100644 --- a/src/proguard/classfile/util/ClassSubHierarchyInitializer.java +++ b/core/src/proguard/classfile/util/ClassSubHierarchyInitializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java b/core/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java similarity index 93% rename from src/proguard/classfile/util/ClassSuperHierarchyInitializer.java rename to core/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java index b5b5a0826..75a26f29b 100644 --- a/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java +++ b/core/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -50,6 +50,16 @@ public class ClassSuperHierarchyInitializer private final WarningPrinter dependencyWarningPrinter; + /** + * Creates a new ClassSuperHierarchyInitializer that initializes the super + * hierarchy of all visited class files. + */ + public ClassSuperHierarchyInitializer(ClassPool programClassPool, + ClassPool libraryClassPool) + { + this(programClassPool, libraryClassPool, null, null); + } + /** * Creates a new ClassSuperHierarchyInitializer that initializes the super * hierarchy of all visited class files, optionally printing warnings if diff --git a/src/proguard/classfile/util/ClassUtil.java b/core/src/proguard/classfile/util/ClassUtil.java similarity index 71% rename from src/proguard/classfile/util/ClassUtil.java rename to core/src/proguard/classfile/util/ClassUtil.java index 439c65ad5..e9efd6072 100644 --- a/src/proguard/classfile/util/ClassUtil.java +++ b/core/src/proguard/classfile/util/ClassUtil.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -63,59 +63,61 @@ public static int internalClassVersion(int majorVersion, int minorVersion) /** * Returns the major part of the given class version number. - * @param classVersion the combined class version number. + * @param internalClassVersion the combined class version number. * @return the major part of the class version number. */ - public static int internalMajorClassVersion(int classVersion) + public static int internalMajorClassVersion(int internalClassVersion) { - return classVersion >>> 16; + return internalClassVersion >>> 16; } /** * Returns the internal class version number. - * @param classVersion the external class version number. + * @param internalClassVersion the external class version number. * @return the internal class version number. */ - public static int internalMinorClassVersion(int classVersion) + public static int internalMinorClassVersion(int internalClassVersion) { - return classVersion & 0xffff; + return internalClassVersion & 0xffff; } /** * Returns the internal class version number. - * @param classVersion the external class version number. + * @param externalClassVersion the external class version number. * @return the internal class version number. */ - public static int internalClassVersion(String classVersion) + public static int internalClassVersion(String externalClassVersion) { return - classVersion.equals(JavaConstants.CLASS_VERSION_1_0) || - classVersion.equals(JavaConstants.CLASS_VERSION_1_1) ? ClassConstants.CLASS_VERSION_1_0 : - classVersion.equals(JavaConstants.CLASS_VERSION_1_2) ? ClassConstants.CLASS_VERSION_1_2 : - classVersion.equals(JavaConstants.CLASS_VERSION_1_3) ? ClassConstants.CLASS_VERSION_1_3 : - classVersion.equals(JavaConstants.CLASS_VERSION_1_4) ? ClassConstants.CLASS_VERSION_1_4 : - classVersion.equals(JavaConstants.CLASS_VERSION_1_5_ALIAS) || - classVersion.equals(JavaConstants.CLASS_VERSION_1_5) ? ClassConstants.CLASS_VERSION_1_5 : - classVersion.equals(JavaConstants.CLASS_VERSION_1_6_ALIAS) || - classVersion.equals(JavaConstants.CLASS_VERSION_1_6) ? ClassConstants.CLASS_VERSION_1_6 : - classVersion.equals(JavaConstants.CLASS_VERSION_1_7_ALIAS) || - classVersion.equals(JavaConstants.CLASS_VERSION_1_7) ? ClassConstants.CLASS_VERSION_1_7 : - classVersion.equals(JavaConstants.CLASS_VERSION_1_8_ALIAS) || - classVersion.equals(JavaConstants.CLASS_VERSION_1_8) ? ClassConstants.CLASS_VERSION_1_8 : - 0; + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_0) || + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_1) ? ClassConstants.CLASS_VERSION_1_0 : + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_2) ? ClassConstants.CLASS_VERSION_1_2 : + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_3) ? ClassConstants.CLASS_VERSION_1_3 : + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_4) ? ClassConstants.CLASS_VERSION_1_4 : + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_5_ALIAS) || + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_5) ? ClassConstants.CLASS_VERSION_1_5 : + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_6_ALIAS) || + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_6) ? ClassConstants.CLASS_VERSION_1_6 : + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_7_ALIAS) || + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_7) ? ClassConstants.CLASS_VERSION_1_7 : + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_8_ALIAS) || + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_8) ? ClassConstants.CLASS_VERSION_1_8 : + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_9_ALIAS) || + externalClassVersion.equals(JavaConstants.CLASS_VERSION_1_9) ? ClassConstants.CLASS_VERSION_1_9 : + 0; } /** * Returns the minor part of the given class version number. - * @param classVersion the combined class version number. + * @param internalClassVersion the combined class version number. * @return the minor part of the class version number. */ - public static String externalClassVersion(int classVersion) + public static String externalClassVersion(int internalClassVersion) { - switch (classVersion) + switch (internalClassVersion) { case ClassConstants.CLASS_VERSION_1_0: return JavaConstants.CLASS_VERSION_1_0; case ClassConstants.CLASS_VERSION_1_2: return JavaConstants.CLASS_VERSION_1_2; @@ -125,6 +127,7 @@ public static String externalClassVersion(int classVersion) case ClassConstants.CLASS_VERSION_1_6: return JavaConstants.CLASS_VERSION_1_6; case ClassConstants.CLASS_VERSION_1_7: return JavaConstants.CLASS_VERSION_1_7; case ClassConstants.CLASS_VERSION_1_8: return JavaConstants.CLASS_VERSION_1_8; + case ClassConstants.CLASS_VERSION_1_9: return JavaConstants.CLASS_VERSION_1_9; default: return null; } } @@ -132,20 +135,20 @@ public static String externalClassVersion(int classVersion) /** * Checks whether the given class version number is supported. - * @param classVersion the combined class version number. + * @param internalClassVersion the combined class version number. * @throws UnsupportedOperationException when the version is not supported. */ - public static void checkVersionNumbers(int classVersion) throws UnsupportedOperationException + public static void checkVersionNumbers(int internalClassVersion) throws UnsupportedOperationException { - if (classVersion < ClassConstants.CLASS_VERSION_1_0 || - classVersion > ClassConstants.CLASS_VERSION_1_8) + if (internalClassVersion < ClassConstants.CLASS_VERSION_1_0 || + internalClassVersion > ClassConstants.CLASS_VERSION_1_9) { - throw new UnsupportedOperationException("Unsupported class version number ["+ - internalMajorClassVersion(classVersion)+"."+ - internalMinorClassVersion(classVersion)+"] (maximum "+ - ClassConstants.CLASS_VERSION_1_8_MAJOR+"."+ - ClassConstants.CLASS_VERSION_1_8_MINOR+", Java "+ - JavaConstants.CLASS_VERSION_1_8+")"); + throw new UnsupportedOperationException("Unsupported version number ["+ + internalMajorClassVersion(internalClassVersion)+"."+ + internalMinorClassVersion(internalClassVersion)+"] (maximum "+ + ClassConstants.CLASS_VERSION_1_9_MAJOR+"."+ + ClassConstants.CLASS_VERSION_1_9_MINOR+", Java "+ + JavaConstants.CLASS_VERSION_1_9+")"); } } @@ -300,6 +303,20 @@ public static boolean isInternalPrimitiveType(char internalType) } + /** + * 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(String internalType) + { + return isInternalPrimitiveType(internalType.charAt(0)); + } + + /** * Returns whether the given internal type is a primitive Category 2 type. * @param internalType the internal type, @@ -372,6 +389,30 @@ public static String internalArrayTypeFromClassName(String internalClassName, } + /** + * Returns the internal array type of a given type, with a given number of + * additional dimensions. + * @param internalType the internal class name, + * e.g. "[Ljava/lang/Object;". + * @param dimensionDelta the number of additional array dimensions, + * e.g. 1. + * @return the internal array type of the array elements, + * e.g. "[[Ljava/lang/Object;". + */ + public static String internalArrayTypeFromType(String internalType, + int dimensionDelta) + { + StringBuffer buffer = new StringBuffer(internalType.length() + dimensionDelta); + + for (int dimension = 0; dimension < dimensionDelta; dimension++) + { + buffer.append(ClassConstants.TYPE_ARRAY); + } + + return buffer.append(internalType).toString(); + } + + /** * Returns the internal element type of a given internal array type. * @param internalArrayType the internal array type, @@ -384,7 +425,48 @@ public static String internalArrayTypeFromClassName(String internalClassName, public static String internalTypeFromArrayType(String internalArrayType) { int index = internalArrayType.lastIndexOf(ClassConstants.TYPE_ARRAY); - return internalArrayType.substring(index+1); + return internalArrayType.substring(index + 1); + } + + + /** + * Returns the internal class type (class name or array type) of a given + * internal type (including an array type). This is the type that can be + * stored in a class constant. + * @param internalType the internal class type, + * e.g. "[I", + * "[Ljava/lang/Object;", or + * "Ljava/lang/Object;". + * @return the internal class name, + * e.g. "[I", + * "[Ljava/lang/Object;", or + * "java/lang/Object". + */ + public static String internalClassTypeFromType(String internalType) + { + return isInternalArrayType(internalType) ? + internalType : + internalClassNameFromClassType(internalType); + } + + + /** + * Returns the internal type of of a given class type (class name or array + * type). This is the type that can be stored in a class constant. + * @param internalType the internal class type, + * e.g. "[I", + * "[Ljava/lang/Object;", or + * "java/lang/Object". + * @return the internal class name, + * e.g. "[I", + * "[Ljava/lang/Object;", or + * "Ljava/lang/Object;". + */ + public static String internalTypeFromClassType(String internalType) + { + return isInternalArrayType(internalType) ? + internalType : + internalTypeFromClassName(internalType); } @@ -435,11 +517,68 @@ public static String internalClassNameFromType(String internalClassType) } + /** + * Returns the internal numeric (or void or array) class name corresponding + * to the given internal primitive type. + * @param internalPrimitiveType the internal class type, + * e.g. "I" or + * "V". + * @return the internal class name, + * e.g. "java/lang/Integer" or + * java/lang/Void. + */ + public static String internalNumericClassNameFromPrimitiveType(char internalPrimitiveType) + { + switch (internalPrimitiveType) + { + case ClassConstants.TYPE_VOID: return ClassConstants.NAME_JAVA_LANG_VOID; + case ClassConstants.TYPE_BOOLEAN: return ClassConstants.NAME_JAVA_LANG_BOOLEAN; + case ClassConstants.TYPE_BYTE: return ClassConstants.NAME_JAVA_LANG_BYTE; + case ClassConstants.TYPE_CHAR: return ClassConstants.NAME_JAVA_LANG_CHARACTER; + case ClassConstants.TYPE_SHORT: return ClassConstants.NAME_JAVA_LANG_SHORT; + case ClassConstants.TYPE_INT: return ClassConstants.NAME_JAVA_LANG_INTEGER; + case ClassConstants.TYPE_LONG: return ClassConstants.NAME_JAVA_LANG_LONG; + case ClassConstants.TYPE_FLOAT: return ClassConstants.NAME_JAVA_LANG_FLOAT; + case ClassConstants.TYPE_DOUBLE: return ClassConstants.NAME_JAVA_LANG_DOUBLE; + case ClassConstants.TYPE_ARRAY: return ClassConstants.NAME_JAVA_LANG_REFLECT_ARRAY; + default: + throw new IllegalArgumentException("Unexpected primitive type ["+internalPrimitiveType+"]"); + } + } + + + /** + * Returns the internal numeric (or void or array) class name corresponding + * to the given internal primitive type. + * @param internalPrimitiveClassName the internal class name, + * e.g. "java/lang/Integer" or + * java/lang/Void. + * @return the internal class type, + * e.g. "I" or + * "V". + */ + public static char internalPrimitiveTypeFromNumericClassName(String internalPrimitiveClassName) + { + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_VOID)) return ClassConstants.TYPE_VOID; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_BOOLEAN)) return ClassConstants.TYPE_BOOLEAN; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_BYTE)) return ClassConstants.TYPE_BYTE; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_CHARACTER)) return ClassConstants.TYPE_CHAR; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_SHORT)) return ClassConstants.TYPE_SHORT; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_INTEGER)) return ClassConstants.TYPE_INT; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_LONG)) return ClassConstants.TYPE_LONG; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_FLOAT)) return ClassConstants.TYPE_FLOAT; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_DOUBLE)) return ClassConstants.TYPE_DOUBLE; + if (internalPrimitiveClassName.equals(ClassConstants.NAME_JAVA_LANG_REFLECT_ARRAY)) return ClassConstants.TYPE_ARRAY; + + throw new IllegalArgumentException("Unexpected primitive class name ["+internalPrimitiveClassName+"]"); + } + + /** * Returns whether the given method name refers to a class initializer or * an instance initializer. * @param internalMethodName the internal method name, - * e.g. "<clinit>". + * e.g. "<clinit>". * @return whether the method name refers to an initializer, * e.g. true. */ @@ -473,7 +612,40 @@ public static String internalMethodReturnType(String internalMethodDescriptor) */ public static int internalMethodParameterCount(String internalMethodDescriptor) { - int counter = 0; + return internalMethodParameterCount(internalMethodDescriptor, true); + } + + + /** + * Returns the number of parameters of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(ID)Z". + * @param accessFlags the access flags of the method, + * e.g. 0. + * @return the number of parameters, + * e.g. 3. + */ + public static int internalMethodParameterCount(String internalMethodDescriptor, + int accessFlags) + { + return internalMethodParameterCount(internalMethodDescriptor, + (accessFlags & ClassConstants.ACC_STATIC) != 0); + } + + + /** + * Returns the number of parameters of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "(ID)Z". + * @param isStatic specifies whether the method is static, + * e.g. false. + * @return the number of parameters, + * e.g. 3. + */ + public static int internalMethodParameterCount(String internalMethodDescriptor, + boolean isStatic) + { + int counter = isStatic ? 0 : 1; int index = 1; while (true) @@ -712,10 +884,12 @@ public static int externalArrayTypeDimensionCount(String externalType) /** * Converts an internal type into an external type. * @param internalType the internal type, - * e.g. "[[Ljava/lang/Object;" or + * e.g. "Ljava/lang/Object;" or + * "[[Ljava/lang/Object;" or * "[I". * @return the external type, - * e.g. "java.lang.Object[][]" or + * e.g. "java.lang.Object" or + * "java.lang.Object[][]" or * "int[]". */ public static String externalType(String internalType) @@ -759,6 +933,26 @@ public static String externalType(String internalType) } + /** + * Converts an internal type into an external type, as expected by + * Class.forName. + * @param internalType the internal type, + * e.g. "Ljava/lang/Object;" or + * "[[Ljava/lang/Object;" or + * "[I". + * @return the external type, + * e.g. "java.lang.Object" or + * "[[Ljava.lang.Object;" or + * "[I". + */ + public static String externalClassForNameType(String internalType) + { + return isInternalArrayType(internalType) ? + externalClassName(internalType) : + externalClassName(internalClassNameFromClassType(internalType)); + } + + /** * Returns whether the given internal descriptor String represents a method * descriptor. @@ -862,6 +1056,34 @@ public static String internalMethodDescriptor(String externalReturnType, } + /** + * Converts the given internal method return type and List of arguments to + * an internal method descriptor. + * + * @param internalReturnType the external method return type, + * e.g. "Z". + * @param internalArguments the external method arguments, + * e.g. { "I", "I" }. + * @return the internal method descriptor, e.g. "(II)Z". + */ + public static String internalMethodDescriptorFromInternalTypes(String internalReturnType, + List internalArguments) + { + StringBuilder internalMethodDescriptor = new StringBuilder(); + internalMethodDescriptor.append(ClassConstants.METHOD_ARGUMENTS_OPEN); + + for (String argument : internalArguments) + { + internalMethodDescriptor.append(argument); + } + + internalMethodDescriptor.append(ClassConstants.METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalReturnType); + + return internalMethodDescriptor.toString(); + } + + /** * Converts an internal field description into an external full field description. * @param accessFlags the access flags of the field. @@ -963,7 +1185,7 @@ public static String externalClassAccessFlags(int accessFlags, String prefix) { string.append(prefix).append(JavaConstants.ACC_FINAL).append(' '); } - if ((accessFlags & ClassConstants.ACC_ANNOTATTION) != 0) + if ((accessFlags & ClassConstants.ACC_ANNOTATION) != 0) { string.append(prefix).append(JavaConstants.ACC_ANNOTATION); } @@ -983,6 +1205,10 @@ else if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) { string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).append(' '); } + else if ((accessFlags & ClassConstants.ACC_MODULE) != 0) + { + string.append(prefix).append(JavaConstants.ACC_MODULE).append(' '); + } return string.toString(); } @@ -1194,6 +1420,190 @@ public static String externalMethodReturnType(String internalMethodDescriptor) } + /** + * Converts internal module access flags into an external access + * description. + * @param accessFlags the module access flags. + * @return the external module access description, + * e.g. "open mandated ". + */ + public static String externalModuleAccessFlags(int accessFlags) + { + return externalModuleAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal module access flags into an external access + * description. + * @param accessFlags the module access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external module access description, + * e.g. "final mandated ". + */ + public static String externalModuleAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.ACC_OPEN) != 0) + { + string.append(prefix).append(JavaConstants.ACC_OPEN).append(' '); + } + if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) + { + string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).append(' '); + } + if ((accessFlags & ClassConstants.ACC_MANDATED) != 0) + { + string.append(prefix).append(JavaConstants.ACC_MANDATED).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal module requires access flags into an external access + * description. + * @param accessFlags the module requires access flags. + * @return the external module requires access description, + * e.g. "static mandated ". + */ + public static String externalRequiresAccessFlags(int accessFlags) + { + return externalRequiresAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal module requires access flags into an external access + * description. + * @param accessFlags the module requires access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external module requires access description, + * e.g. "static mandated ". + */ + public static String externalRequiresAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.ACC_TRANSITIVE) != 0) + { + string.append(prefix).append(JavaConstants.ACC_TRANSITIVE).append(' '); + } + if ((accessFlags & ClassConstants.ACC_STATIC_PHASE) != 0) + { + string.append(prefix).append(JavaConstants.ACC_STATIC).append(' '); + } + if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) + { + string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).append(' '); + } + if ((accessFlags & ClassConstants.ACC_MANDATED) != 0) + { + string.append(prefix).append(JavaConstants.ACC_MANDATED).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal module exports access flags into an external access + * description. + * @param accessFlags the module exports access flags. + * @return the external module exports access description, + * e.g. "synthetic mandated ". + */ + public static String externalExportsAccessFlags(int accessFlags) + { + return externalExportsAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal module exports access flags into an external access + * description. + * @param accessFlags the module exports access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external module exports access description, + * e.g. "static mandated ". + */ + public static String externalExportsAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) + { + string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).append(' '); + } + if ((accessFlags & ClassConstants.ACC_MANDATED) != 0) + { + string.append(prefix).append(JavaConstants.ACC_MANDATED).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal module opens access flags into an external access + * description. + * @param accessFlags the module opens access flags. + * @return the external module opens access description, + * e.g. "synthetic mandated ". + */ + public static String externalOpensAccessFlags(int accessFlags) + { + return externalOpensAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal module opens access flags into an external access + * description. + * @param accessFlags the module opens access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external module opens access description, + * e.g. "static mandated ". + */ + public static String externalOpensAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.ACC_SYNTHETIC) != 0) + { + string.append(prefix).append(JavaConstants.ACC_SYNTHETIC).append(' '); + } + if ((accessFlags & ClassConstants.ACC_MANDATED) != 0) + { + string.append(prefix).append(JavaConstants.ACC_MANDATED).append(' '); + } + + return string.toString(); + } + + /** * Converts an internal class name, method name, and method descriptor to * an external method return type and name. diff --git a/src/proguard/classfile/util/DescriptorClassEnumeration.java b/core/src/proguard/classfile/util/DescriptorClassEnumeration.java similarity index 99% rename from src/proguard/classfile/util/DescriptorClassEnumeration.java rename to core/src/proguard/classfile/util/DescriptorClassEnumeration.java index 2be640c69..3a820cb58 100644 --- a/src/proguard/classfile/util/DescriptorClassEnumeration.java +++ b/core/src/proguard/classfile/util/DescriptorClassEnumeration.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/DynamicClassReferenceInitializer.java b/core/src/proguard/classfile/util/DynamicClassReferenceInitializer.java similarity index 99% rename from src/proguard/classfile/util/DynamicClassReferenceInitializer.java rename to core/src/proguard/classfile/util/DynamicClassReferenceInitializer.java index c7e1c9b0d..cb1954321 100644 --- a/src/proguard/classfile/util/DynamicClassReferenceInitializer.java +++ b/core/src/proguard/classfile/util/DynamicClassReferenceInitializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java b/core/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java new file mode 100644 index 000000000..662037d2e --- /dev/null +++ b/core/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java @@ -0,0 +1,971 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.AttributeVisitor; +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.visitor.*; +import proguard.util.StringMatcher; + +/** + * This AttributeVisitor initializes any constant class member references of all + * code that it visits. It currently handles invocations of + * Class#get[Declared]{Field,Constructor,Method} and + * Atomic{Integer,Long,Reference}FieldUpdater.newUpdater + * with constant string arguments. It lets the corresponding string constants + * refer to their class members in the program class pool or in the library + * class pool. It may create new string constants and update the code, in order + * to avoid clashes between identically named class members. + *

+ * 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 AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + MemberVisitor +{ + /* + private static boolean DEBUG = true; + /*/ + private static final boolean DEBUG = false; + //*/ + + private static final int CLASS_INDEX = InstructionSequenceMatcher.A; + private static final int MEMBER_NAME_INDEX = InstructionSequenceMatcher.B; + private static final int MEMBER_TYPE_INDEX = InstructionSequenceMatcher.C; + + + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter notePrinter; + private final StringMatcher noteFieldExceptionMatcher; + private final StringMatcher noteMethodExceptionMatcher; + + + // Retrieving fields or methods from known, constant classes. + private final InstructionSequenceMatcher knownItegerUpdaterMatcher; + private final InstructionSequenceMatcher knownLongUpdaterMatcher; + private final InstructionSequenceMatcher knownReferenceUpdaterMatcher; + + // Retrieving fields or methods from unknown classes. + private final InstructionSequenceMatcher unknownIntegerUpdaterMatcher; + private final InstructionSequenceMatcher unknownLongUpdaterMatcher; + private final InstructionSequenceMatcher unknownReferenceUpdaterMatcher; + + private final MyDynamicMemberFinder dynamicMemberFinder = new MyDynamicMemberFinder(); + + private final MemberFinder memberFinder = new MemberFinder(true); + private final MemberFinder declaredMemberFinder = new MemberFinder(false); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + + // Fields acting as parameters for the visitors. + private Clazz referencedClass; + + + /** + * 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; + + // Create the instruction sequences and matchers. + InstructionSequenceBuilder builder = + new InstructionSequenceBuilder(programClassPool, libraryClassPool); + + // AtomicIntegerFieldUpdater.newUpdater(A.class, "someField"). + Instruction[] knownItegerUpdaterInstructions = builder + .ldc_(CLASS_INDEX) + .ldc_(MEMBER_NAME_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER, + ClassConstants.METHOD_NAME_NEW_UPDATER, + ClassConstants.METHOD_TYPE_NEW_INTEGER_UPDATER) + .instructions(); + + // AtomicLongFieldUpdater.newUpdater(A.class, "someField"). + Instruction[] knownLongUpdaterInstructions = builder + .ldc_(CLASS_INDEX) + .ldc_(MEMBER_NAME_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER, + ClassConstants.METHOD_NAME_NEW_UPDATER, + ClassConstants.METHOD_TYPE_NEW_LONG_UPDATER) + .instructions(); + + // AtomicReferenceFieldUpdater.newUpdater(A.class, B.class, "someField"). + Instruction[] knownReferenceUpdaterInstructions = builder + .ldc_(CLASS_INDEX) + .ldc_(MEMBER_TYPE_INDEX) + .ldc_(MEMBER_NAME_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER, + ClassConstants.METHOD_NAME_NEW_UPDATER, + ClassConstants.METHOD_TYPE_NEW_REFERENCE_UPDATER) + .instructions(); + + // AtomicIntegerFieldUpdater.newUpdater(..., "someField"). + Instruction[] unknownIntegerUpdaterInstructions = builder + .ldc_(MEMBER_NAME_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER, + ClassConstants.METHOD_NAME_NEW_UPDATER, + ClassConstants.METHOD_TYPE_NEW_INTEGER_UPDATER) + .instructions(); + + // AtomicLongFieldUpdater.newUpdater(..., "someField"). + Instruction[] unknownLongUpdaterInstructions = builder + .ldc_(MEMBER_NAME_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER, + ClassConstants.METHOD_NAME_NEW_UPDATER, + ClassConstants.METHOD_TYPE_NEW_LONG_UPDATER) + .instructions(); + + // AtomicReferenceFieldUpdater.newUpdater(..., "someField"). + final Instruction[] unknownReferenceUpdaterInstructions = builder + .ldc_(MEMBER_NAME_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER, + ClassConstants.METHOD_NAME_NEW_UPDATER, + ClassConstants.METHOD_TYPE_NEW_REFERENCE_UPDATER) + .instructions(); + + Constant[] constants = builder.constants(); + + knownItegerUpdaterMatcher = new InstructionSequenceMatcher(constants, knownItegerUpdaterInstructions); + knownLongUpdaterMatcher = new InstructionSequenceMatcher(constants, knownLongUpdaterInstructions); + knownReferenceUpdaterMatcher = new InstructionSequenceMatcher(constants, knownReferenceUpdaterInstructions); + unknownIntegerUpdaterMatcher = new InstructionSequenceMatcher(constants, unknownIntegerUpdaterInstructions); + unknownLongUpdaterMatcher = new InstructionSequenceMatcher(constants, unknownLongUpdaterInstructions); + unknownReferenceUpdaterMatcher = new InstructionSequenceMatcher(constants, unknownReferenceUpdaterInstructions); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println("DynamicMemberReferenceInitializer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + } + + // Set up the code attribute editor. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // Find the dynamic class member references. + codeAttribute.instructionsAccept(clazz, method, this); + + // Apply any 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) + { + // Try to match get[Declared]{Field,Constructor,Method} constructs. + instruction.accept(clazz, method, codeAttribute, offset, dynamicMemberFinder); + + // Try to match the AtomicIntegerFieldUpdater.newUpdater( + // SomeClass.class, "someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + knownItegerUpdaterMatcher, + unknownIntegerUpdaterMatcher, true, false, false, + "" + ClassConstants.TYPE_INT); + + // Try to match the AtomicLongFieldUpdater.newUpdater( + // SomeClass.class, "someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + knownLongUpdaterMatcher, + unknownLongUpdaterMatcher, true, false, false, + "" + ClassConstants.TYPE_LONG); + + // Try to match the AtomicReferenceFieldUpdater.newUpdater( + // SomeClass.class, SomeClass.class, "someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + knownReferenceUpdaterMatcher, + unknownReferenceUpdaterMatcher, true, false, false, + 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 isConstructor, + boolean isDeclared, + String memberDescriptor) + { + 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()) + { + // Retrieve the offset of the instruction that loads the member + // name. It's currently always the last but one instruction. + int memberNameInstructionOffset = + constantSequenceMatcher.matchedInstructionOffset( + constantSequenceMatcher.instructionCount() - 2); + + // Get the member's class. + int classIndex = constantSequenceMatcher.matchedConstantIndex(CLASS_INDEX); + clazz.constantPoolEntryAccept(classIndex, this); + + if (referencedClass != null) + { + // Get the field's type, if applicable. + int typeClassIndex = constantSequenceMatcher.matchedConstantIndex(MEMBER_TYPE_INDEX); + if (typeClassIndex > 0) + { + memberDescriptor = + ClassUtil.internalTypeFromClassName(clazz.getClassName(typeClassIndex)); + } + + // Get the member's name. + int memberNameIndex = constantSequenceMatcher.matchedConstantIndex(MEMBER_NAME_INDEX); + String memberName = clazz.getStringString(memberNameIndex); + + // Create a new string constant and update the instruction. + initializeDynamicMemberReference(clazz, + memberNameInstructionOffset, + referencedClass, + memberName, + memberDescriptor, + isField, + isConstructor, + isDeclared); + } + + // 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()) + { + int memberNameIndex = variableSequenceMatcher.matchedConstantIndex(MEMBER_NAME_INDEX); + String memberName = clazz.getStringString(memberNameIndex); + + // Print out a note about the dynamic invocation. + printDynamicMemberAccessNote(clazz, + memberName, + memberDescriptor, + isField, + isConstructor, + isDeclared); + } + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Remember the referenced class. + referencedClass = ClassUtil.isInternalArrayType(classConstant.getName(clazz)) ? + null : + classConstant.referencedClass; + } + + + // Small utility methods. + + /** + * Creates a new string constant for the specified referenced class member, + * and updates the instruction that loads it. + */ + private void initializeDynamicMemberReference(Clazz clazz, + int memberNameInstructionOffset, + Clazz referencedClass, + String memberName, + String memberDescriptor, + boolean isField, + boolean isConstructor, + boolean isDeclared) + { + // See if we can find the referenced class member locally, or + // somewhere in the hierarchy. + MemberFinder referencedMemberFinder = isDeclared ? + declaredMemberFinder : + memberFinder; + + Member referencedMember = + referencedMemberFinder.findMember(referencedClass, + memberName, + memberDescriptor, + isField); + + if (DEBUG) + { + System.out.println("DynamicMemberReferenceInitializer: ["+clazz.getName()+"] matched string ["+memberName+"]: in ["+referencedClass+"] -> ["+referencedMember+"]"); + } + + if (referencedMember != null) + { + if (!isDeclared) + { + referencedClass = referencedMemberFinder.correspondingClass(); + } + + // Update the string constant. + //stringConstant.referencedMember = referencedMember; + //stringConstant.referencedClass = referencedClass; + + // Create a new string constant with the found references. + int stringConstantIndex = + new ConstantPoolEditor((ProgramClass)clazz).addStringConstant(memberName, + referencedClass, + referencedMember); + + // Update the instruction. + codeAttributeEditor.replaceInstruction(memberNameInstructionOffset, + new ConstantInstruction(InstructionConstants.OP_LDC, + stringConstantIndex)); + } + } + + + /** + * Prints out a note on the matched dynamic invocation of a constructor. + */ + private void printDynamicConstructorAccessNote(Clazz clazz, + Clazz referencedClass, + String memberDescriptor, + boolean isDeclared) + { + // 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? + if (noteMethodExceptionMatcher == null || + !noteMethodExceptionMatcher.matches(ClassConstants.METHOD_NAME_INIT)) + { + // Print out the actual note. + notePrinter.print(clazz.getName(), + "Note: " + + ClassUtil.externalClassName(clazz.getName()) + + " retrieves a " + + (isDeclared ? "declared " : "") + + "constructor '" + + ClassConstants.METHOD_NAME_INIT + + JavaConstants.METHOD_ARGUMENTS_OPEN + + ClassUtil.externalMethodArguments(memberDescriptor) + + JavaConstants.METHOD_ARGUMENTS_CLOSE + + "' dynamically"); + + // Print out notes about potential candidates. + ClassVisitor classVisitor = + new AllMethodVisitor( + new MemberNameFilter(ClassConstants.METHOD_NAME_INIT, + new MemberDescriptorFilter(memberDescriptor, this))); + + if (referencedClass != null) + { + referencedClass.hierarchyAccept(true, !isDeclared, false, false, + classVisitor); + } + else + { + programClassPool.classesAcceptAlphabetically(classVisitor); + libraryClassPool.classesAcceptAlphabetically(classVisitor); + } + } + } + } + + + /** + * Prints out a note on the matched dynamic access to a class member. + */ + private void printDynamicMemberAccessNote(Clazz clazz, + String memberName, + String memberDescriptor, + boolean isField, + boolean isConstructor, + boolean isDeclared) + { + // 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; + + if (noteExceptionMatcher == null || + !noteExceptionMatcher.matches(memberName)) + { + // Compose the external member name and partial descriptor. + String externalMemberDescription = memberName; + + if (!isField) + { + externalMemberDescription += + JavaConstants.METHOD_ARGUMENTS_OPEN + + ClassUtil.externalMethodArguments(memberDescriptor) + + JavaConstants.METHOD_ARGUMENTS_CLOSE; + } + + // Print out the actual note. + notePrinter.print(clazz.getName(), + "Note: " + + ClassUtil.externalClassName(clazz.getName()) + + " accesses a " + + (isDeclared ? "declared " : "") + + (isField ? "field" : + isConstructor ? "constructor" : + "method") + + " '" + + externalMemberDescription + + "' dynamically"); + + // Print out notes about potential candidates. + ClassVisitor classVisitor; + + if (isField) + { + classVisitor = memberDescriptor == null ? + new AllFieldVisitor( + new MemberNameFilter(memberName, this)) : + new AllFieldVisitor( + new MemberNameFilter(memberName, + new MemberDescriptorFilter(memberDescriptor, this))); + } + else + { + classVisitor = + new AllMethodVisitor( + new MemberNameFilter(memberName, + new MemberDescriptorFilter(memberDescriptor, 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)) + + "; }'"); + } + } + + + /** + * This InstructionVisitor finds get[Declared]{Field,Constructor,Method} + * constructs with constant arguments. It then makes sure the class member + * name strings point to the class members, or it prints out notes about the + * possible alternatives. + */ + private class MyDynamicMemberFinder + extends SimplifiedVisitor + implements InstructionVisitor, + ConstantVisitor + { + private static final int LABEL_START = 0; // ldc SomeClass.class + private static final int LABEL_LOAD_MEMBER_NAME = 1; // [ ldc "someMethod"/"someField" ] + private static final int LABEL_LOAD_CLASS_ARRAY_SIZE = 2; // [ sipush #someParameterCount + private static final int LABEL_CREATE_CLASS_ARRAY = 3; // anewarray java/lang/Class + private static final int LABEL_DUP_CLASS_ARRAY = 4; // [ dup + private static final int LABEL_LOAD_PARAMETER_INDEX = 5; // sipush #someParameterIndex + private static final int LABEL_LOAD_PARAMETER_TYPE = 6; // ldc SomeParameterClass.class / getstatic java/lang/SomePrimitive.TYPE + private static final int LABEL_STORE_PARAMETER = 7; // aastore ]* ] / aconst_null + private static final int LABEL_GET_MEMBER = 8; // invokevirtual java/lang/Class.getField/getConstructor/getMethod + + private int label; + private int instructionOffset; + private int memberNameInstructionOffset; + private Clazz referencedClass; + private String memberName; + private int parameterCount; + private int parameterIndex; + private StringBuffer parameterTypes = new StringBuffer(); + + + public void reset() + { + label = LABEL_START; + referencedClass = null; + memberName = null; + parameterTypes.setLength(0); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + if (DEBUG) + { + System.out.println("Label ["+label+"] A "+instruction.toString(offset)); + } + + reset(); + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + if (DEBUG) + { + System.out.println("Label ["+label+"] S "+simpleInstruction.toString(offset)); + } + + int transition = label | simpleInstruction.canonicalOpcode() << 8; + + switch (transition) + { + case LABEL_START | InstructionConstants.OP_ICONST_0 << 8: + case LABEL_LOAD_MEMBER_NAME | InstructionConstants.OP_ICONST_0 << 8: + case LABEL_CREATE_CLASS_ARRAY | InstructionConstants.OP_ICONST_0 << 8: + case LABEL_DUP_CLASS_ARRAY | InstructionConstants.OP_ICONST_0 << 8: + case LABEL_LOAD_PARAMETER_TYPE | InstructionConstants.OP_ICONST_0 << 8: + case LABEL_STORE_PARAMETER | InstructionConstants.OP_ICONST_0 << 8: + case LABEL_GET_MEMBER | InstructionConstants.OP_ICONST_0 << 8: + // This could be the start of creating a class array. + reset(); + parameterCount = simpleInstruction.constant; + label = LABEL_CREATE_CLASS_ARRAY; + break; + + case LABEL_LOAD_CLASS_ARRAY_SIZE | InstructionConstants.OP_ICONST_0 << 8: + parameterCount = simpleInstruction.constant; + label = LABEL_CREATE_CLASS_ARRAY; + break; + + case LABEL_LOAD_CLASS_ARRAY_SIZE | InstructionConstants.OP_ACONST_NULL << 8: + parameterCount = 0; + label = LABEL_GET_MEMBER; + break; + + case LABEL_DUP_CLASS_ARRAY | InstructionConstants.OP_DUP << 8: + label = LABEL_LOAD_PARAMETER_INDEX; + break; + + case LABEL_LOAD_PARAMETER_INDEX | InstructionConstants.OP_ICONST_0 << 8: + // Is it pushing the expected parameter index? + if (parameterIndex == simpleInstruction.constant) + { + label = LABEL_LOAD_PARAMETER_TYPE; + } + else + { + // This could be the start of creating a class array. + reset(); + parameterCount = simpleInstruction.constant; + label = LABEL_CREATE_CLASS_ARRAY; + } + break; + + case LABEL_STORE_PARAMETER | InstructionConstants.OP_AASTORE << 8: + // Are we still expecting more parameters? + label = ++parameterIndex < parameterCount ? + LABEL_DUP_CLASS_ARRAY : + LABEL_GET_MEMBER; + break; + + default: + reset(); + break; + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + if (DEBUG) + { + System.out.println("Label ["+label+"] C "+constantInstruction.toString(offset)); + } + + // Let the constant figure out the transition. + switch (constantInstruction.canonicalOpcode()) + { + case InstructionConstants.OP_LDC: + instructionOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_INVOKEVIRTUAL: + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + + case InstructionConstants.OP_ANEWARRAY: + if (label == LABEL_CREATE_CLASS_ARRAY) + { + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + } + else + { + reset(); + } + break; + + default: + reset(); + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + reset(); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Argument of ldc or anewarray instruction. + switch (label) + { + case LABEL_START: + referencedClass = classConstant.referencedClass; + + label = LABEL_LOAD_MEMBER_NAME; + break; + + case LABEL_CREATE_CLASS_ARRAY: + if (classConstant.getName(clazz).equals(ClassConstants.NAME_JAVA_LANG_CLASS)) + { + parameterIndex = 0; + label = parameterCount > 0 ? + LABEL_DUP_CLASS_ARRAY : + LABEL_GET_MEMBER; + } + else + { + referencedClass = classConstant.referencedClass; + + label = LABEL_LOAD_MEMBER_NAME; + } + break; + + case LABEL_LOAD_PARAMETER_TYPE: + String parameterType = + ClassUtil.internalTypeFromClassType(classConstant.getName(clazz)); + + parameterTypes.append(parameterType); + + label = LABEL_STORE_PARAMETER; + break; + + default: + // For other states, we'll treat this as a potential + // initial class name. + referencedClass = classConstant.referencedClass; + + label = LABEL_LOAD_MEMBER_NAME; + break; + } + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Argument of ldc instruction. + switch (label) + { + case LABEL_LOAD_MEMBER_NAME: + break; + + default: + // For other states, we'll treat this as a potential + // initial method name, without a known class. + referencedClass = null; + break; + } + + // Whatever state, we'll treat this as a potential method name. + memberNameInstructionOffset = instructionOffset; + memberName = stringConstant.getString(clazz); + + label = LABEL_LOAD_CLASS_ARRAY_SIZE; + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + // Argument of getstatic instruction. + switch (label) + { + case LABEL_LOAD_PARAMETER_TYPE: + String className = fieldrefConstant.getClassName(clazz); + String fieldName = fieldrefConstant.getName(clazz); + String fieldType = fieldrefConstant.getType(clazz); + + if (className.startsWith(ClassConstants.PACKAGE_JAVA_LANG) && + fieldName.equals(ClassConstants.FIELD_NAME_TYPE) && + fieldType.equals(ClassConstants.FIELD_TYPE_TYPE)) + { + char parameterType = + ClassUtil.internalPrimitiveTypeFromNumericClassName(className); + + parameterTypes.append(parameterType); + + label = LABEL_STORE_PARAMETER; + } + else + { + reset(); + } + break; + + default: + reset(); + break; + } + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + // Argument of invokevirtual instruction. + String className = methodrefConstant.getClassName(clazz); + + if (className.equals(ClassConstants.NAME_JAVA_LANG_CLASS)) + { + String methodName = methodrefConstant.getName(clazz); + String methodType = methodrefConstant.getType(clazz); + + if (label == LABEL_LOAD_CLASS_ARRAY_SIZE && + methodType.equals(ClassConstants.METHOD_TYPE_CLASS_GET_FIELD) && + memberName != null) + { + if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_FIELD)) + { + resolveMemberString(clazz, true, false, false); + } + else if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_DECLARED_FIELD)) + { + resolveMemberString(clazz, true, false, true); + } + else + { + reset(); + } + } + else if (label == LABEL_GET_MEMBER && + methodType.equals(ClassConstants.METHOD_TYPE_CLASS_GET_CONSTRUCTOR)) + { + if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_CONSTRUCTOR)) + { + resolveMemberString(clazz, false, true, false); + } + else if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_DECLARED_CONSTRUCTOR)) + { + resolveMemberString(clazz, false, true, true); + } + else + { + reset(); + } + } + else if (label == LABEL_GET_MEMBER && + methodType.equals(ClassConstants.METHOD_TYPE_CLASS_GET_METHOD) && + memberName != null) + { + if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_METHOD)) + { + resolveMemberString(clazz, false, false, false); + } + else if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_DECLARED_METHOD)) + { + resolveMemberString(clazz, false, false, true); + } + else + { + reset(); + } + } + else + { + reset(); + } + } + else + { + reset(); + } + } + + + /** + * Links the referenced class member in the string, or prints out + * notes about the possible alternatives. + */ + private void resolveMemberString(Clazz clazz, + boolean isField, + boolean isConstructor, + boolean isDeclared) + { + String memberDescriptor = isField ? + null : + ClassConstants.METHOD_ARGUMENTS_OPEN + + parameterTypes.toString() + + ClassConstants.METHOD_ARGUMENTS_CLOSE + + "L***;"; + + if (DEBUG) + { + System.out.println("DynamicMemberReferenceInitializer: found member access"); + System.out.println(" isField = "+isField); + System.out.println(" isConstructor = "+isConstructor); + System.out.println(" isDeclared = "+isDeclared); + System.out.println(" referenced class = "+(referencedClass == null ? "(none)" : "["+referencedClass.getName()+"]")); + System.out.println(" member name = "+(memberName == null ? "(none)" : "["+memberName+"]")); + System.out.println(" member descriptor = "+(memberDescriptor == null ? "(none)" : "["+memberDescriptor+"]")); + } + + if (referencedClass != null) + { + if (isConstructor) + { + // We currently can't fill out some reference to a + // constructor. Just print out notes instead. + printDynamicConstructorAccessNote(clazz, + referencedClass, + memberDescriptor, + isDeclared); + } + else + { + // Create a new string constant and update the instruction. + initializeDynamicMemberReference(clazz, + memberNameInstructionOffset, + referencedClass, + memberName, + memberDescriptor, + isField, + isConstructor, + isDeclared); + } + } + else + { + // Print out notes about the method in some unknown class. + printDynamicMemberAccessNote(clazz, + isConstructor ? + ClassConstants.METHOD_NAME_INIT : + memberName, + memberDescriptor, + isField, + isConstructor, + isDeclared); + } + } + } +} \ No newline at end of file diff --git a/src/proguard/classfile/util/EnumFieldReferenceInitializer.java b/core/src/proguard/classfile/util/EnumFieldReferenceInitializer.java similarity index 99% rename from src/proguard/classfile/util/EnumFieldReferenceInitializer.java rename to core/src/proguard/classfile/util/EnumFieldReferenceInitializer.java index a633d43ae..e574a2a27 100644 --- a/src/proguard/classfile/util/EnumFieldReferenceInitializer.java +++ b/core/src/proguard/classfile/util/EnumFieldReferenceInitializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/ExternalTypeEnumeration.java b/core/src/proguard/classfile/util/ExternalTypeEnumeration.java similarity index 98% rename from src/proguard/classfile/util/ExternalTypeEnumeration.java rename to core/src/proguard/classfile/util/ExternalTypeEnumeration.java index 0575a7bde..1dfe8db9c 100644 --- a/src/proguard/classfile/util/ExternalTypeEnumeration.java +++ b/core/src/proguard/classfile/util/ExternalTypeEnumeration.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/InstructionSequenceMatcher.java b/core/src/proguard/classfile/util/InstructionSequenceMatcher.java similarity index 84% rename from src/proguard/classfile/util/InstructionSequenceMatcher.java rename to core/src/proguard/classfile/util/InstructionSequenceMatcher.java index 22aadfa0d..da59455ce 100644 --- a/src/proguard/classfile/util/InstructionSequenceMatcher.java +++ b/core/src/proguard/classfile/util/InstructionSequenceMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -45,8 +45,8 @@ public class InstructionSequenceMatcher private static final boolean DEBUG = false; private static final boolean DEBUG_MORE = false; /*/ - public static boolean DEBUG = true; - public static boolean DEBUG_MORE = true; + public static boolean DEBUG = System.getProperty("ism") != null; + public static boolean DEBUG_MORE = System.getProperty("ismm") != null; //*/ public static final int X = 0x40000000; @@ -57,24 +57,38 @@ public class InstructionSequenceMatcher 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; + public static final int E = 0x40000007; + public static final int F = 0x40000008; + public static final int G = 0x40000009; + public static final int H = 0x4000000a; + public static final int I = 0x4000000b; + public static final int J = 0x4000000c; + public static final int K = 0x4000000d; + public static final int L = 0x4000000e; + public static final int M = 0x4000000f; + public static final int N = 0x40000010; + public static final int O = 0x40000011; + public static final int P = 0x40000012; + public static final int Q = 0x40000013; + public static final int R = 0x40000014; + + + protected final Constant[] patternConstants; + protected 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 int[] matchedArguments = new int[21]; 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; + protected Constant patternConstant; + protected boolean matchingConstant; /** @@ -404,6 +418,20 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + //PrimitiveArrayConstant primitiveArrayPatternConstant = (PrimitiveArrayConstant)patternConstant; + // + //// Compare the primitive array values. + //matchingConstant = + // primitiveArrayConstant.getLength() == primitiveArrayPatternConstant.getLength() && + // ArrayUtil.equal(primitiveArrayConstant.getValues(), + // primitiveArrayPatternConstant.getValues(), + // primitiveArrayPatternConstant.getLength()); + throw new UnsupportedOperationException(); + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { StringConstant stringPatternConstant = (StringConstant)patternConstant; @@ -511,8 +539,8 @@ public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTyp // Small utility methods. - private boolean matchingOpcodes(Instruction instruction1, - Instruction instruction2) + protected boolean matchingOpcodes(Instruction instruction1, + Instruction instruction2) { // Check the opcode. return instruction1.opcode == instruction2.opcode || @@ -520,8 +548,8 @@ private boolean matchingOpcodes(Instruction instruction1, } - private boolean matchingArguments(int argument1, - int argument2) + protected boolean matchingArguments(int argument1, + int argument2) { int argumentIndex = argument2 - X; if (argumentIndex < 0) @@ -566,8 +594,8 @@ private boolean isMatchingArgumentIndex(int argumentIndex) } - private boolean matchingArguments(int[] arguments1, - int[] arguments2) + protected boolean matchingArguments(int[] arguments1, + int[] arguments2) { if (arguments1.length != arguments2.length) { @@ -586,9 +614,9 @@ private boolean matchingArguments(int[] arguments1, } - private boolean matchingConstantIndices(Clazz clazz, - int constantIndex1, - int constantIndex2) + protected boolean matchingConstantIndices(Clazz clazz, + int constantIndex1, + int constantIndex2) { if (constantIndex2 >= X) { @@ -646,9 +674,9 @@ private boolean isMatchingConstantIndex(int constantIndex) } - private boolean matchingBranchOffsets(int offset, - int branchOffset1, - int branchOffset2) + protected boolean matchingBranchOffsets(int offset, + int branchOffset1, + int branchOffset2) { int argumentIndex = branchOffset2 - X; if (argumentIndex < 0) @@ -671,9 +699,9 @@ else if (!isMatchingArgumentIndex(argumentIndex)) } - private boolean matchingJumpOffsets(int offset, - int[] jumpOffsets1, - int[] jumpOffsets2) + protected boolean matchingJumpOffsets(int offset, + int[] jumpOffsets1, + int[] jumpOffsets2) { if (jumpOffsets1.length != jumpOffsets2.length) { @@ -703,7 +731,7 @@ private void checkMatch(boolean condition, { 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)); + System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+instruction.toString(offset)+(condition?"\t== ":"\t ")+patternInstructions[patternInstructionIndex].toString(patternInstructionIndex)); } // Did the instruction match? @@ -720,6 +748,9 @@ private void checkMatch(boolean condition, if (matching) { + // Allow subclasses to perform a final check on additional constraints. + matching &= finalMatch(clazz, method, codeAttribute, offset, instruction); + if (DEBUG) { System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); @@ -727,6 +758,22 @@ private void checkMatch(boolean condition, { System.out.println(" "+InstructionFactory.create(codeAttribute.code, matchedInstructionOffsets[index]).toString(matchedInstructionOffsets[index])); } + + for (int index = 0; index < matchedArguments.length; index++) + { + if ((matchedArgumentFlags & (1 << index)) != 0) + { + System.out.println(" Arg #"+index+": "+matchedArguments[index]); + } + } + + for (int index = 0; index < matchedConstantIndices.length; index++) + { + if (isMatchingConstantIndex(index)) + { + System.out.println(" Constant #"+index+": "+matchedConstantIndices[index]); + } + } } // Start matching from the first instruction again next time. @@ -751,4 +798,27 @@ private void checkMatch(boolean condition, } } } + + + /** + * Performs a final check on the candidate sequence to match, + * after the pattern has been successfully fully matched with the + * sequence. Subclasses may override this method to implement + * additional constraints on the matched sequences. + * + * @param clazz + * @param method + * @param codeAttribute + * @param offset + * @param instruction + * @return + */ + protected boolean finalMatch(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction ) + { + return true; + } } diff --git a/src/proguard/classfile/util/InternalTypeEnumeration.java b/core/src/proguard/classfile/util/InternalTypeEnumeration.java similarity index 99% rename from src/proguard/classfile/util/InternalTypeEnumeration.java rename to core/src/proguard/classfile/util/InternalTypeEnumeration.java index d1a41d969..70c989855 100644 --- a/src/proguard/classfile/util/InternalTypeEnumeration.java +++ b/core/src/proguard/classfile/util/InternalTypeEnumeration.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/MemberFinder.java b/core/src/proguard/classfile/util/MemberFinder.java similarity index 61% rename from src/proguard/classfile/util/MemberFinder.java rename to core/src/proguard/classfile/util/MemberFinder.java index d6eaa4ac5..32db2678f 100644 --- a/src/proguard/classfile/util/MemberFinder.java +++ b/core/src/proguard/classfile/util/MemberFinder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -36,13 +36,46 @@ public class MemberFinder private static class MemberFoundException extends RuntimeException {} private static final MemberFoundException MEMBER_FOUND = new MemberFoundException(); + private final boolean searchHierarchy; + private Clazz clazz; private Member member; + /** + * Creates a new MemberFinder that looks in the class hierarchy. + */ + public MemberFinder() + { + this(true); + } + + + /** + * Creates a new MemberFinder that looks in the class hierarchy if + * specified. + */ + public MemberFinder(boolean searchHierarchy) + { + this.searchHierarchy = searchHierarchy; + } + + /** * Finds the field with the given name and descriptor in the given - * class or its hierarchy. + * class or its hierarchy. The name and descriptor may contain wildcards. + */ + public Field findField(Clazz clazz, + String name, + String descriptor) + { + return findField(null, clazz, name, descriptor); + } + + + /** + * Finds the field with the given name and descriptor in the given + * class or its hierarchy. The name and descriptor may contain wildcards. */ public Field findField(Clazz referencingClass, Clazz clazz, @@ -55,7 +88,19 @@ public Field findField(Clazz referencingClass, /** * Finds the method with the given name and descriptor in the given - * class or its hierarchy. + * class or its hierarchy. The name and descriptor may contain wildcards. + */ + public Method findMethod(Clazz clazz, + String name, + String descriptor) + { + return findMethod(null, clazz, name, descriptor); + } + + + /** + * Finds the method with the given name and descriptor in the given + * class or its hierarchy. The name and descriptor may contain wildcards. */ public Method findMethod(Clazz referencingClass, Clazz clazz, @@ -68,7 +113,21 @@ public Method findMethod(Clazz referencingClass, /** * Finds the class member with the given name and descriptor in the given - * class or its hierarchy. + * class or its hierarchy. The name and descriptor may contain wildcards. + */ + public Member findMember(Clazz clazz, + String name, + String descriptor, + boolean isField) + { + return findMember(null, clazz, name, descriptor, isField); + } + + + /** + * Finds the class member with the given name and descriptor in the given + * class or its hierarchy, referenced from the optional given class. + * The name and descriptor may contain wildcards. */ public Member findMember(Clazz referencingClass, Clazz clazz, @@ -81,13 +140,42 @@ public Member findMember(Clazz referencingClass, // compiled with "-target 1.2" or higher (the default in JDK 1.4). try { + boolean containsWildcards = + (name != null && (name.indexOf('*') >= 0 || name.indexOf('?') >= 0)) || + (descriptor != null && (descriptor.indexOf('*') >= 0 || descriptor.indexOf('?') >= 0)); + 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))); + + // Check the accessibility from the referencing class, if any + // (non-dummy). + MemberVisitor memberVisitor = + referencingClass != null && + referencingClass.getName() != null ? + new MemberClassAccessFilter(referencingClass, this) : + this; + + clazz.hierarchyAccept(true, + searchHierarchy, + searchHierarchy, + false, + containsWildcards ? + isField ? + new AllFieldVisitor( + new MemberNameFilter(name, + new MemberDescriptorFilter(descriptor, + memberVisitor))) : + + new AllMethodVisitor( + new MemberNameFilter(name, + new MemberDescriptorFilter(descriptor, + memberVisitor))) : + isField ? + new NamedFieldVisitor(name, descriptor, + memberVisitor) : + + new NamedMethodVisitor(name, descriptor, + memberVisitor)); } catch (MemberFoundException ex) { diff --git a/src/proguard/classfile/util/MethodLinker.java b/core/src/proguard/classfile/util/MethodLinker.java similarity index 98% rename from src/proguard/classfile/util/MethodLinker.java rename to core/src/proguard/classfile/util/MethodLinker.java index 5eaa2d3ee..8ddb762ea 100644 --- a/src/proguard/classfile/util/MethodLinker.java +++ b/core/src/proguard/classfile/util/MethodLinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/util/PrimitiveArrayConstantReplacer.java b/core/src/proguard/classfile/util/PrimitiveArrayConstantReplacer.java new file mode 100644 index 000000000..d6c9fbd96 --- /dev/null +++ b/core/src/proguard/classfile/util/PrimitiveArrayConstantReplacer.java @@ -0,0 +1,218 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.*; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor replaces all PrimitiveArray constants by Java bytecode + * compliant array store instructions. + * + * @see ArrayInitializationReplacer + * @author Thomas Neidhart + */ +public class PrimitiveArrayConstantReplacer +extends SimplifiedVisitor +implements ClassVisitor, + AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + PrimitiveArrayConstantElementVisitor +{ + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + private final ConstantPoolShrinker constantPoolShrinker = new ConstantPoolShrinker(); + + // Fields acting as parameters and return values. + + private boolean classModified; + private InstructionSequenceBuilder builder; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + ConstantCounter counter = new ConstantCounter(); + programClass.constantPoolEntriesAccept( + new ConstantTagFilter(ClassConstants.CONSTANT_PrimitiveArray, + counter)); + + // Replace PrimitiveArray constants if the class has any. + if (counter.getCount() > 0) + { + classModified = false; + + programClass.methodsAccept(new AllAttributeVisitor(this)); + + if (classModified) + { + // Remove the now unused PrimitiveArray constants. + programClass.accept(constantPoolShrinker); + } + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + codeAttribute.instructionsAccept(clazz, method, this); + + if (codeAttributeEditor.isModified()) + { + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + + classModified = 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) + { + builder = null; + + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + + if (builder != null) + { + codeAttributeEditor.replaceInstruction(offset, builder.instructions()); + + classModified = true; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + char primitiveType = primitiveArrayConstant.getPrimitiveType(); + int arrayLength = primitiveArrayConstant.getLength(); + + // Start composing a new array initialization sequence. + builder = new InstructionSequenceBuilder((ProgramClass) clazz); + + // Push the primitive array length. + builder.pushInt(primitiveArrayConstant.getLength()); + + // Create the primitive array. + builder.newarray(InstructionUtil.arrayTypeFromInternalType(primitiveType)); + + // Fill out the primitive array elements. + primitiveArrayConstant.primitiveArrayElementsAccept(clazz, this); + } + + + // Implementations for PrimitiveArrayConstantElementVisitor. + + public void visitBooleanArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, boolean value) + { + builder.dup() + .pushInt(index) + .iconst(value ? 1 : 0) + .bastore(); + } + + + public void visitByteArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, byte value) + { + builder.dup() + .pushInt(index) + .pushInt(value) + .bastore(); + } + + + public void visitCharArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, char value) + { + builder.dup() + .pushInt(index) + .pushInt(value) + .castore(); + } + + + public void visitShortArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, short value) + { + builder.dup() + .pushInt(index) + .pushInt(value) + .sastore(); + } + + + public void visitIntArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, int value) + { + builder.dup() + .pushInt(index) + .pushInt(value) + .iastore(); + } + + + public void visitFloatArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, float value) + { + builder.dup() + .pushInt(index) + .pushFloat(value) + .fastore(); + } + + + public void visitLongArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, long value) + { + builder.dup() + .pushInt(index) + .pushLong(value) + .lastore(); + } + + + public void visitDoubleArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, double value) + { + builder.dup() + .pushInt(index) + .pushDouble(value) + .dastore(); + } +} diff --git a/src/proguard/classfile/util/SimplifiedVisitor.java b/core/src/proguard/classfile/util/SimplifiedVisitor.java similarity index 88% rename from src/proguard/classfile/util/SimplifiedVisitor.java rename to core/src/proguard/classfile/util/SimplifiedVisitor.java index c18135a04..55f2a31c4 100644 --- a/src/proguard/classfile/util/SimplifiedVisitor.java +++ b/core/src/proguard/classfile/util/SimplifiedVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,6 +24,7 @@ import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; import proguard.classfile.attribute.annotation.target.*; +import proguard.classfile.attribute.module.*; import proguard.classfile.attribute.preverification.*; import proguard.classfile.constant.*; import proguard.classfile.instruction.*; @@ -146,6 +147,12 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + visitAnyConstant(clazz, primitiveArrayConstant); + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { visitAnyConstant(clazz, stringConstant); @@ -169,6 +176,16 @@ public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHa visitAnyConstant(clazz, methodHandleConstant); } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + visitAnyConstant(clazz, moduleConstant); + } + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + visitAnyConstant(clazz, packageConstant); + } + /** * Visits any type of RefConstant of the given class. @@ -224,6 +241,118 @@ public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTyp } + // Simplifications for PrimitiveArrayConstantVisitor. + + public void visitAnyPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, Object values) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitBooleanArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, boolean[] values) + { + visitAnyPrimitiveArrayConstant(clazz, primitiveArrayConstant, values); + } + + + public void visitByteArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, byte[] values) + { + visitAnyPrimitiveArrayConstant(clazz, primitiveArrayConstant, values); + } + + + public void visitCharArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, char[] values) + { + visitAnyPrimitiveArrayConstant(clazz, primitiveArrayConstant, values); + } + + + public void visitShortArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, short[] values) + { + visitAnyPrimitiveArrayConstant(clazz, primitiveArrayConstant, values); + } + + + public void visitIntArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int[] values) + { + visitAnyPrimitiveArrayConstant(clazz, primitiveArrayConstant, values); + } + + + public void visitFloatArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, float[] values) + { + visitAnyPrimitiveArrayConstant(clazz, primitiveArrayConstant, values); + } + + + public void visitLongArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, long[] values) + { + visitAnyPrimitiveArrayConstant(clazz, primitiveArrayConstant, values); + } + + + public void visitDoubleArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, double[] values) + { + visitAnyPrimitiveArrayConstant(clazz, primitiveArrayConstant, values); + } + + + // Simplifications for PrimitiveArrayConstantElementVisitor. + + public void visitAnyPrimitiveArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitBooleanArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, boolean value) + { + visitAnyPrimitiveArrayConstantElement(clazz, primitiveArrayConstant, index); + } + + + public void visitByteArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, byte value) + { + visitAnyPrimitiveArrayConstantElement(clazz, primitiveArrayConstant, index); + } + + + public void visitCharArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, char value) + { + visitAnyPrimitiveArrayConstantElement(clazz, primitiveArrayConstant, index); + } + + + public void visitShortArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, short value) + { + visitAnyPrimitiveArrayConstantElement(clazz, primitiveArrayConstant, index); + } + + + public void visitIntArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, int value) + { + visitAnyPrimitiveArrayConstantElement(clazz, primitiveArrayConstant, index); + } + + + public void visitFloatArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, float value) + { + visitAnyPrimitiveArrayConstantElement(clazz, primitiveArrayConstant, index); + } + + + public void visitLongArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, long value) + { + visitAnyPrimitiveArrayConstantElement(clazz, primitiveArrayConstant, index); + } + + + public void visitDoubleArrayConstantElement(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant, int index, double value) + { + visitAnyPrimitiveArrayConstantElement(clazz, primitiveArrayConstant, index); + } + + // Simplifications for AttributeVisitor. /** @@ -271,6 +400,24 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + visitAnyAttribute(clazz, moduleAttribute); + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + visitAnyAttribute(clazz, moduleMainClassAttribute); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + visitAnyAttribute(clazz, modulePackagesAttribute); + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { visitAnyAttribute(clazz, deprecatedAttribute); diff --git a/src/proguard/classfile/util/StringReferenceInitializer.java b/core/src/proguard/classfile/util/StringReferenceInitializer.java similarity index 98% rename from src/proguard/classfile/util/StringReferenceInitializer.java rename to core/src/proguard/classfile/util/StringReferenceInitializer.java index 2cc103255..46376c617 100644 --- a/src/proguard/classfile/util/StringReferenceInitializer.java +++ b/core/src/proguard/classfile/util/StringReferenceInitializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/StringSharer.java b/core/src/proguard/classfile/util/StringSharer.java similarity index 99% rename from src/proguard/classfile/util/StringSharer.java rename to core/src/proguard/classfile/util/StringSharer.java index d2d5023df..4cf100eb5 100644 --- a/src/proguard/classfile/util/StringSharer.java +++ b/core/src/proguard/classfile/util/StringSharer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/WarningPrinter.java b/core/src/proguard/classfile/util/WarningPrinter.java similarity index 98% rename from src/proguard/classfile/util/WarningPrinter.java rename to core/src/proguard/classfile/util/WarningPrinter.java index c4bc23247..c2298cd12 100644 --- a/src/proguard/classfile/util/WarningPrinter.java +++ b/core/src/proguard/classfile/util/WarningPrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/util/package.html b/core/src/proguard/classfile/util/package.html similarity index 100% rename from src/proguard/classfile/util/package.html rename to core/src/proguard/classfile/util/package.html diff --git a/src/proguard/classfile/visitor/AllClassVisitor.java b/core/src/proguard/classfile/visitor/AllClassVisitor.java similarity index 96% rename from src/proguard/classfile/visitor/AllClassVisitor.java rename to core/src/proguard/classfile/visitor/AllClassVisitor.java index e7f670fd4..c2471d9cb 100644 --- a/src/proguard/classfile/visitor/AllClassVisitor.java +++ b/core/src/proguard/classfile/visitor/AllClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/AllFieldVisitor.java b/core/src/proguard/classfile/visitor/AllFieldVisitor.java similarity index 96% rename from src/proguard/classfile/visitor/AllFieldVisitor.java rename to core/src/proguard/classfile/visitor/AllFieldVisitor.java index a449089d3..c86a0d21e 100644 --- a/src/proguard/classfile/visitor/AllFieldVisitor.java +++ b/core/src/proguard/classfile/visitor/AllFieldVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/AllMemberVisitor.java b/core/src/proguard/classfile/visitor/AllMemberVisitor.java similarity index 96% rename from src/proguard/classfile/visitor/AllMemberVisitor.java rename to core/src/proguard/classfile/visitor/AllMemberVisitor.java index 92e31d501..c3aa55727 100644 --- a/src/proguard/classfile/visitor/AllMemberVisitor.java +++ b/core/src/proguard/classfile/visitor/AllMemberVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/AllMethodVisitor.java b/core/src/proguard/classfile/visitor/AllMethodVisitor.java similarity index 96% rename from src/proguard/classfile/visitor/AllMethodVisitor.java rename to core/src/proguard/classfile/visitor/AllMethodVisitor.java index af34fea5c..346c4fa45 100644 --- a/src/proguard/classfile/visitor/AllMethodVisitor.java +++ b/core/src/proguard/classfile/visitor/AllMethodVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/BottomClassFilter.java b/core/src/proguard/classfile/visitor/BottomClassFilter.java similarity index 97% rename from src/proguard/classfile/visitor/BottomClassFilter.java rename to core/src/proguard/classfile/visitor/BottomClassFilter.java index 89f240716..5a0c41863 100644 --- a/src/proguard/classfile/visitor/BottomClassFilter.java +++ b/core/src/proguard/classfile/visitor/BottomClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassAccessFilter.java b/core/src/proguard/classfile/visitor/ClassAccessFilter.java similarity index 98% rename from src/proguard/classfile/visitor/ClassAccessFilter.java rename to core/src/proguard/classfile/visitor/ClassAccessFilter.java index 3637270d3..ffca52fea 100644 --- a/src/proguard/classfile/visitor/ClassAccessFilter.java +++ b/core/src/proguard/classfile/visitor/ClassAccessFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassCleaner.java b/core/src/proguard/classfile/visitor/ClassCleaner.java similarity index 99% rename from src/proguard/classfile/visitor/ClassCleaner.java rename to core/src/proguard/classfile/visitor/ClassCleaner.java index dffe11820..8f2ee4b89 100644 --- a/src/proguard/classfile/visitor/ClassCleaner.java +++ b/core/src/proguard/classfile/visitor/ClassCleaner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassCollector.java b/core/src/proguard/classfile/visitor/ClassCollector.java similarity index 96% rename from src/proguard/classfile/visitor/ClassCollector.java rename to core/src/proguard/classfile/visitor/ClassCollector.java index 1a65fccb0..0e9b628cd 100644 --- a/src/proguard/classfile/visitor/ClassCollector.java +++ b/core/src/proguard/classfile/visitor/ClassCollector.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassCounter.java b/core/src/proguard/classfile/visitor/ClassCounter.java similarity index 96% rename from src/proguard/classfile/visitor/ClassCounter.java rename to core/src/proguard/classfile/visitor/ClassCounter.java index 850a64cf0..d765c3532 100644 --- a/src/proguard/classfile/visitor/ClassCounter.java +++ b/core/src/proguard/classfile/visitor/ClassCounter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassHierarchyTraveler.java b/core/src/proguard/classfile/visitor/ClassHierarchyTraveler.java similarity index 98% rename from src/proguard/classfile/visitor/ClassHierarchyTraveler.java rename to core/src/proguard/classfile/visitor/ClassHierarchyTraveler.java index a14e1320c..8511121c4 100644 --- a/src/proguard/classfile/visitor/ClassHierarchyTraveler.java +++ b/core/src/proguard/classfile/visitor/ClassHierarchyTraveler.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassNameFilter.java b/core/src/proguard/classfile/visitor/ClassNameFilter.java similarity index 55% rename from src/proguard/classfile/visitor/ClassNameFilter.java rename to core/src/proguard/classfile/visitor/ClassNameFilter.java index 297e8fa7f..3db4756ab 100644 --- a/src/proguard/classfile/visitor/ClassNameFilter.java +++ b/core/src/proguard/classfile/visitor/ClassNameFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -40,30 +40,66 @@ public class ClassNameFilter implements 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. + * @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), + this(regularExpression, null, classVisitor); + } + + + /** + * Creates a new ClassNameFilter. + * @param regularExpression the regular expression against which class + * names will be matched. + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + * @param classVisitor the ClassVisitor to which + * visits will be delegated. + */ + public ClassNameFilter(String regularExpression, + List variableStringMatchers, + ClassVisitor classVisitor) + { + this(new ListParser(new ClassNameParser(variableStringMatchers)).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. + * @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(regularExpression, null, classVisitor); + } + + + /** + * Creates a new ClassNameFilter. + * @param regularExpression the regular expression against which class + * names will be matched. + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + * @param classVisitor the ClassVisitor to which + * visits will be delegated. */ public ClassNameFilter(List regularExpression, + List variableStringMatchers, ClassVisitor classVisitor) { - this(new ListParser(new ClassNameParser()).parse(regularExpression), + this(new ListParser(new ClassNameParser(variableStringMatchers)).parse(regularExpression), classVisitor); } diff --git a/core/src/proguard/classfile/visitor/ClassPoolClassVisitor.java b/core/src/proguard/classfile/visitor/ClassPoolClassVisitor.java new file mode 100644 index 000000000..0b0f83002 --- /dev/null +++ b/core/src/proguard/classfile/visitor/ClassPoolClassVisitor.java @@ -0,0 +1,70 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 ClassPoolVisitor and ClassVisitor remembers the ClassPool instances + * that it visits and applies the given ClassPoolVisitor to the most + * recently remembered one, every time it visits a Clazz instance. + * + * @author Eric Lafortune + */ +public class ClassPoolClassVisitor +implements ClassPoolVisitor, + ClassVisitor +{ + private ClassPoolVisitor classPoolVisitor; + private ClassPool classPool; + + + /** + * Creates a new ClassPoolClassVisitor. + * @param classPoolVisitor + */ + public ClassPoolClassVisitor(ClassPoolVisitor classPoolVisitor) + { + this.classPoolVisitor = classPoolVisitor; + } + + + // Implementations for ClassPoolVisitor. + + public void visitClassPool(ClassPool classPool) + { + this.classPool = classPool; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + classPoolVisitor.visitClassPool(classPool); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + classPoolVisitor.visitClassPool(classPool); + } +} diff --git a/src/proguard/classfile/visitor/ClassPoolFiller.java b/core/src/proguard/classfile/visitor/ClassPoolFiller.java similarity index 96% rename from src/proguard/classfile/visitor/ClassPoolFiller.java rename to core/src/proguard/classfile/visitor/ClassPoolFiller.java index b6c535217..94c599018 100644 --- a/src/proguard/classfile/visitor/ClassPoolFiller.java +++ b/core/src/proguard/classfile/visitor/ClassPoolFiller.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassPoolRemover.java b/core/src/proguard/classfile/visitor/ClassPoolRemover.java similarity index 96% rename from src/proguard/classfile/visitor/ClassPoolRemover.java rename to core/src/proguard/classfile/visitor/ClassPoolRemover.java index 71e421d8f..c2afda145 100644 --- a/src/proguard/classfile/visitor/ClassPoolRemover.java +++ b/core/src/proguard/classfile/visitor/ClassPoolRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassPoolVisitor.java b/core/src/proguard/classfile/visitor/ClassPoolVisitor.java similarity index 95% rename from src/proguard/classfile/visitor/ClassPoolVisitor.java rename to core/src/proguard/classfile/visitor/ClassPoolVisitor.java index ae6a6ac6c..b37f6f739 100644 --- a/src/proguard/classfile/visitor/ClassPoolVisitor.java +++ b/core/src/proguard/classfile/visitor/ClassPoolVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassPresenceFilter.java b/core/src/proguard/classfile/visitor/ClassPresenceFilter.java similarity index 98% rename from src/proguard/classfile/visitor/ClassPresenceFilter.java rename to core/src/proguard/classfile/visitor/ClassPresenceFilter.java index 115b37ba9..592db812e 100644 --- a/src/proguard/classfile/visitor/ClassPresenceFilter.java +++ b/core/src/proguard/classfile/visitor/ClassPresenceFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassPrinter.java b/core/src/proguard/classfile/visitor/ClassPrinter.java similarity index 84% rename from src/proguard/classfile/visitor/ClassPrinter.java rename to core/src/proguard/classfile/visitor/ClassPrinter.java index c14a67d9a..38014ca56 100644 --- a/src/proguard/classfile/visitor/ClassPrinter.java +++ b/core/src/proguard/classfile/visitor/ClassPrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,6 +26,8 @@ import proguard.classfile.attribute.annotation.target.*; import proguard.classfile.attribute.annotation.target.visitor.*; import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.module.*; +import proguard.classfile.attribute.module.visitor.*; import proguard.classfile.attribute.preverification.*; import proguard.classfile.attribute.preverification.visitor.*; import proguard.classfile.attribute.visitor.*; @@ -35,7 +37,7 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; -import java.io.PrintStream; +import java.io.PrintWriter; /** @@ -59,6 +61,10 @@ public class ClassPrinter ParameterInfoVisitor, LocalVariableInfoVisitor, LocalVariableTypeInfoVisitor, + RequiresInfoVisitor, + ExportsInfoVisitor, + OpensInfoVisitor, + ProvidesInfoVisitor, AnnotationVisitor, TypeAnnotationVisitor, TargetInfoVisitor, @@ -69,27 +75,28 @@ public class ClassPrinter { private static final String INDENTATION = " "; - private final PrintStream ps; + private final PrintWriter pw; private int indentation; /** - * Creates a new ClassPrinter that prints to System.out. + * Creates a new ClassPrinter that prints to the standard output. */ public ClassPrinter() { - this(System.out); + // We're using the system's default character encoding for writing to + // the standard output. + this(new PrintWriter(System.out, true)); } /** - * Creates a new ClassPrinter that prints to the given - * PrintStream. + * Creates a new ClassPrinter that prints to the given writer. */ - public ClassPrinter(PrintStream printStream) + public ClassPrinter(PrintWriter printWriter) { - ps = printStream; + pw = printWriter; } @@ -107,11 +114,10 @@ public void visitProgramClass(ProgramClass programClass) println(" = target " + ClassUtil.externalClassVersion(programClass.u4version)); println("Access flags: 0x" + Integer.toHexString(programClass.u2accessFlags)); println(" = " + - ((programClass.u2accessFlags & ClassConstants.ACC_ANNOTATTION) != 0 ? "@ " : "") + ClassUtil.externalClassAccessFlags(programClass.u2accessFlags) + - ((programClass.u2accessFlags & ClassConstants.ACC_ENUM) != 0 ? "enum " : - (programClass.u2accessFlags & ClassConstants.ACC_INTERFACE) == 0 ? "class " : - "") + + ((programClass.u2accessFlags & (ClassConstants.ACC_ENUM | + ClassConstants.ACC_INTERFACE | + ClassConstants.ACC_MODULE)) == 0 ? "class " : "") + ClassUtil.externalClassName(programClass.getName()) + (programClass.u2superClass == 0 ? "" : " extends " + ClassUtil.externalClassName(programClass.getSuperName()))); @@ -159,11 +165,10 @@ public void visitLibraryClass(LibraryClass libraryClass) println("Superclass: " + libraryClass.getSuperName()); println("Access flags: 0x" + Integer.toHexString(libraryClass.u2accessFlags)); println(" = " + - ((libraryClass.u2accessFlags & ClassConstants.ACC_ANNOTATTION) != 0 ? "@ " : "") + ClassUtil.externalClassAccessFlags(libraryClass.u2accessFlags) + - ((libraryClass.u2accessFlags & ClassConstants.ACC_ENUM) != 0 ? "enum " : - (libraryClass.u2accessFlags & ClassConstants.ACC_INTERFACE) == 0 ? "class " : - "") + + ((libraryClass.u2accessFlags & (ClassConstants.ACC_ENUM | + ClassConstants.ACC_INTERFACE | + ClassConstants.ACC_MODULE)) == 0 ? "class " : "") + ClassUtil.externalClassName(libraryClass.getName()) + (libraryClass.getSuperName() == null ? "" : " extends " + ClassUtil.externalClassName(libraryClass.getSuperName()))); @@ -218,6 +223,14 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + println(visitorInfo(primitiveArrayConstant) + " PrimitiveArray " + + primitiveArrayConstant.getPrimitiveType() + "[" + + primitiveArrayConstant.getLength() + "]"); + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { println(visitorInfo(stringConstant) + " String [" + @@ -251,6 +264,19 @@ public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHa outdent(); } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + println(visitorInfo(moduleConstant) + " Module [" + + moduleConstant.getName(clazz) + "]"); + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + println(visitorInfo(packageConstant) + " Package [" + + packageConstant.getName(clazz) + "]"); + } + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { @@ -434,7 +460,7 @@ public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAtt public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) { println(visitorInfo(innerClassesAttribute) + - " Inner classes attribute (count = " + innerClassesAttribute.u2classesCount + ")"); + " Inner classes attribute (count = " + innerClassesAttribute.u2classesCount + "):"); indent(); innerClassesAttribute.innerClassEntriesAccept(clazz, this); @@ -458,6 +484,63 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + println(visitorInfo(moduleAttribute) + + " Module attribute:"); + + indent(); + clazz.constantPoolEntryAccept(moduleAttribute.u2moduleNameIndex, this); + println("Access flags: 0x" + + Integer.toHexString(moduleAttribute.u2moduleFlags) + + " = " + + ClassUtil.externalModuleAccessFlags(moduleAttribute.u2moduleFlags)); + + if (moduleAttribute.u2moduleVersionIndex != 0) + { + clazz.constantPoolEntryAccept(moduleAttribute.u2moduleVersionIndex, this); + } + println("Requires:"); + moduleAttribute.requiresAccept(clazz, this); + println("Exports:"); + moduleAttribute.exportsAccept(clazz, this); + println("Opens:"); + moduleAttribute.opensAccept(clazz, this); + println("Uses services:"); + + for (int index = 0; index < moduleAttribute.u2usesCount; index++) + { + clazz.constantPoolEntryAccept(moduleAttribute.u2uses[index], this); + } + + println("Provides services:"); + moduleAttribute.providesAccept(clazz, this); + outdent(); + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + println(visitorInfo(moduleMainClassAttribute) + + " Module main class attribute:"); + + indent(); + clazz.constantPoolEntryAccept(moduleMainClassAttribute.u2mainClass, this); + outdent(); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + println(visitorInfo(modulePackagesAttribute) + + " Module packages attribute (count = " + modulePackagesAttribute.u2packagesCount + "):"); + + indent(); + modulePackagesAttribute.packagesAccept(clazz, this); + outdent(); + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { println(visitorInfo(deprecatedAttribute) + @@ -495,7 +578,7 @@ public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueA public void visitMethodParametersAttribute(Clazz clazz, Method method, MethodParametersAttribute methodParametersAttribute) { println(visitorInfo(methodParametersAttribute) + - " Method parameters attribute (count = " + methodParametersAttribute.u1parametersCount + ")"); + " Method parameters attribute (count = " + methodParametersAttribute.u1parametersCount + "):"); indent(); methodParametersAttribute.parametersAccept(clazz, method, this); @@ -506,10 +589,10 @@ public void visitMethodParametersAttribute(Clazz clazz, Method method, MethodPar public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) { println(visitorInfo(exceptionsAttribute) + - " Exceptions attribute (count = " + exceptionsAttribute.u2exceptionIndexTableLength + ")"); + " Exceptions attribute (count = " + exceptionsAttribute.u2exceptionIndexTableLength + "):"); indent(); - exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this); + exceptionsAttribute.exceptionEntriesAccept(clazz, this); outdent(); } @@ -567,7 +650,7 @@ public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttrib { println(visitorInfo(lineNumberTableAttribute) + " Line number table attribute (count = " + - lineNumberTableAttribute.u2lineNumberTableLength + ")"); + lineNumberTableAttribute.u2lineNumberTableLength + "):"); indent(); lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); @@ -579,7 +662,7 @@ public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAtt { println(visitorInfo(localVariableTableAttribute) + " Local variable table attribute (count = " + - localVariableTableAttribute.u2localVariableTableLength + ")"); + localVariableTableAttribute.u2localVariableTableLength + "):"); indent(); localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); @@ -591,7 +674,7 @@ public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, Cod { println(visitorInfo(localVariableTypeTableAttribute) + " Local variable type table attribute (count = "+ - localVariableTypeTableAttribute.u2localVariableTypeTableLength + ")"); + localVariableTypeTableAttribute.u2localVariableTypeTableLength + "):"); indent(); localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); @@ -825,7 +908,7 @@ public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAtt moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); - ps.println(", Stack: (empty)"); + pw.println(", Stack: (empty)"); } @@ -837,7 +920,7 @@ public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribu fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); - ps.print(", Stack: "); + pw.print(", Stack: "); fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); @@ -849,55 +932,55 @@ public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribu public void visitIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, IntegerType integerType) { - ps.print("[i]"); + pw.print("[i]"); } public void visitFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FloatType floatType) { - ps.print("[f]"); + pw.print("[f]"); } public void visitLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LongType longType) { - ps.print("[l]"); + pw.print("[l]"); } public void visitDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, DoubleType doubleType) { - ps.print("[d]"); + pw.print("[d]"); } public void visitTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TopType topType) { - ps.print("[T]"); + pw.print("[T]"); } public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType) { - ps.print("[a:" + clazz.getClassName(objectType.u2classIndex) + "]"); + pw.print("[a:" + clazz.getClassName(objectType.u2classIndex) + "]"); } public void visitNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, NullType nullType) { - ps.print("[n]"); + pw.print("[n]"); } public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) { - ps.print("[u:" + uninitializedType.u2newInstructionOffset + "]"); + pw.print("[u:" + uninitializedType.u2newInstructionOffset + "]"); } public void visitUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedThisType uninitializedThisType) { - ps.print("[u:this]"); + pw.print("[u:this]"); } @@ -915,9 +998,9 @@ public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAt public void visitParameterInfo(Clazz clazz, Method method, int parameterIndex, ParameterInfo parameterInfo) { - println("p" + parameterIndex + ": access flags: 0x" + Integer.toHexString(parameterInfo.u2accessFlags) + " = " + - ClassUtil.externalParameterAccessFlags(parameterInfo.u2accessFlags) + - (parameterInfo.u2nameIndex == 0 ? "" : " [" + parameterInfo.getName(clazz) + "]")); + println("p" + parameterIndex + ": Access flags: 0x" + Integer.toHexString(parameterInfo.u2accessFlags) + " = " + + ClassUtil.externalParameterAccessFlags(parameterInfo.u2accessFlags) + " [" + + (parameterInfo.u2nameIndex == 0 ? "" : parameterInfo.getName(clazz)) + "]"); } @@ -945,6 +1028,86 @@ public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute } + // Implementations for RequiresInfoVisitor + + public void visitRequiresInfo(Clazz clazz, RequiresInfo requiresInfo) + { + println(visitorInfo(requiresInfo) + + " RequiresInfo:"); + + indent(); + clazz.constantPoolEntryAccept(requiresInfo.u2requiresIndex, this); + println("Access flags: 0x" + Integer.toHexString(requiresInfo.u2requiresFlags) + " = " + + ClassUtil.externalRequiresAccessFlags(requiresInfo.u2requiresFlags)); + clazz.constantPoolEntryAccept(requiresInfo.u2requiresVersionIndex, this); + outdent(); + } + + + // Implementations for ExportsInfoVisitor + + public void visitExportsInfo(Clazz clazz, ExportsInfo exportsInfo) + { + println(visitorInfo(exportsInfo) + + " ExportsInfo (targets count = " + + exportsInfo.u2exportsToCount + "):"); + + indent(); + clazz.constantPoolEntryAccept(exportsInfo.u2exportsIndex, this); + println("Access flags: 0x" + Integer.toHexString(exportsInfo.u2exportsFlags) + " = " + + ClassUtil.externalExportsAccessFlags(exportsInfo.u2exportsFlags)); + + for (int index = 0; index < exportsInfo.u2exportsToCount; index++) + { + clazz.constantPoolEntryAccept(exportsInfo.u2exportsToIndex[index], this); + } + + outdent(); + } + + + // Implementations for ExportsOpensVisitor + + public void visitOpensInfo(Clazz clazz, OpensInfo opensInfo) + { + println(visitorInfo(opensInfo) + + " OpensInfo (targets count = " + + opensInfo.u2opensToCount + "):"); + + indent(); + clazz.constantPoolEntryAccept(opensInfo.u2opensIndex, this); + println("Access flags: 0x" + Integer.toHexString(opensInfo.u2opensFlags) + " = " + + ClassUtil.externalOpensAccessFlags(opensInfo.u2opensFlags)); + + for (int index = 0; index < opensInfo.u2opensToCount; index++) + { + clazz.constantPoolEntryAccept(opensInfo.u2opensToIndex[index], this); + } + + outdent(); + } + + + // Implementations for ProvidesInfoVisitor + + public void visitProvidesInfo(Clazz clazz, ProvidesInfo providesInfo) + { + println(visitorInfo(providesInfo) + + " ProvidesInfo (with count = " + + providesInfo.u2providesWithCount + "):"); + + indent(); + clazz.constantPoolEntryAccept(providesInfo.u2providesIndex, this); + + for (int index = 0; index < providesInfo.u2providesWithCount; index++) + { + clazz.constantPoolEntryAccept(providesInfo.u2providesWithIndex[index], this); + } + + outdent(); + } + + // Implementations for AnnotationVisitor. public void visitAnnotation(Clazz clazz, Annotation annotation) @@ -958,6 +1121,17 @@ public void visitAnnotation(Clazz clazz, Annotation annotation) } + public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation) + { + println(visitorInfo(annotation) + + " Parameter #"+parameterIndex+", annotation [" + annotation.getType(clazz) + "]:"); + + indent(); + annotation.elementValuesAccept(clazz, this); + outdent(); + } + + // Implementations for TypeAnnotationVisitor. public void visitTypeAnnotation(Clazz clazz, TypeAnnotation typeAnnotation) @@ -1164,15 +1338,15 @@ private void print(String string) { for (int index = 0; index < indentation; index++) { - ps.print(INDENTATION); + pw.print(INDENTATION); } - ps.print(string); + pw.print(string); } private void println() { - ps.println(); + pw.println(); } diff --git a/src/proguard/classfile/visitor/ClassVersionFilter.java b/core/src/proguard/classfile/visitor/ClassVersionFilter.java similarity index 98% rename from src/proguard/classfile/visitor/ClassVersionFilter.java rename to core/src/proguard/classfile/visitor/ClassVersionFilter.java index 50c496172..a9fea4c82 100644 --- a/src/proguard/classfile/visitor/ClassVersionFilter.java +++ b/core/src/proguard/classfile/visitor/ClassVersionFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassVersionSetter.java b/core/src/proguard/classfile/visitor/ClassVersionSetter.java similarity index 97% rename from src/proguard/classfile/visitor/ClassVersionSetter.java rename to core/src/proguard/classfile/visitor/ClassVersionSetter.java index 12e1c59ea..6021c62ab 100644 --- a/src/proguard/classfile/visitor/ClassVersionSetter.java +++ b/core/src/proguard/classfile/visitor/ClassVersionSetter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ClassVisitor.java b/core/src/proguard/classfile/visitor/ClassVisitor.java similarity index 95% rename from src/proguard/classfile/visitor/ClassVisitor.java rename to core/src/proguard/classfile/visitor/ClassVisitor.java index 6358b3b50..b3d3c9807 100644 --- a/src/proguard/classfile/visitor/ClassVisitor.java +++ b/core/src/proguard/classfile/visitor/ClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java b/core/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java similarity index 98% rename from src/proguard/classfile/visitor/ConcreteClassDownTraveler.java rename to core/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java index 1169d8d29..3d92ff908 100644 --- a/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java +++ b/core/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/visitor/ConstructorMethodFilter.java b/core/src/proguard/classfile/visitor/ConstructorMethodFilter.java new file mode 100644 index 000000000..7d6e0e9b5 --- /dev/null +++ b/core/src/proguard/classfile/visitor/ConstructorMethodFilter.java @@ -0,0 +1,141 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; + +/** + * This MemberVisitor delegates its visits to one of three delegates, depending on whether the visited method is: + * + * - a constructor + * - a constructor that calls a super constructor + * - or another method. + * + * @author Johan Leys + */ +public class ConstructorMethodFilter +extends SimplifiedVisitor +implements MemberVisitor, + + // Implementation interfaces. + AttributeVisitor, + InstructionVisitor +{ + private static final int FIELD_INDEX = InstructionSequenceMatcher.X; + + private static final Constant[] CONSTANTS = new Constant[] {}; + + private static final Instruction[] INVOKE_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, FIELD_INDEX), + }; + + private final InstructionSequenceMatcher invokeMatcher = new InstructionSequenceMatcher(CONSTANTS, INVOKE_INSTRUCTIONS); + + private final MemberVisitor superCallingConstructorVisitor; + private final MemberVisitor constructorVisitor; + private final MemberVisitor otherMethodVisitor; + + private boolean isSuperConstructorCalled; + + + public ConstructorMethodFilter(MemberVisitor constructorVisitor) + { + this(constructorVisitor, constructorVisitor, null); + } + + + public ConstructorMethodFilter(MemberVisitor superCallingConstructorVisitor, + MemberVisitor constructorVisitor, + MemberVisitor otherMethodVisitor) + { + this.superCallingConstructorVisitor = superCallingConstructorVisitor; + this.constructorVisitor = constructorVisitor; + this.otherMethodVisitor = otherMethodVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Delegate the visit. + MemberVisitor delegateVisitor = delegateVisitor(programClass, programMethod); + if (delegateVisitor != null) + { + delegateVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + codeAttribute.instructionsAccept(clazz, method, this); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + instruction.accept(clazz, method, codeAttribute, offset, invokeMatcher); + if (invokeMatcher.isMatching()) + { + MethodrefConstant methodrefConstant = (MethodrefConstant)((ProgramClass)clazz).getConstant(invokeMatcher.matchedArgument(FIELD_INDEX)); + if (ClassConstants.METHOD_NAME_INIT.equals(methodrefConstant.getName(clazz))) + { + isSuperConstructorCalled |= + methodrefConstant.getClassName(clazz).equals(clazz.getSuperName()); + } + } + } + + + // Small utility methods. + + private MemberVisitor delegateVisitor(ProgramClass programClass, ProgramMethod programMethod) + { + isSuperConstructorCalled = false; + + if (ClassConstants.METHOD_NAME_INIT.equals(programMethod.getName(programClass))) + { + // Search the code attribute for super. invocations. + programMethod.attributesAccept(programClass, this); + return isSuperConstructorCalled ? superCallingConstructorVisitor : constructorVisitor; + } + else + { + return otherMethodVisitor; + } + } +} diff --git a/src/proguard/classfile/visitor/DotClassClassVisitor.java b/core/src/proguard/classfile/visitor/DotClassClassVisitor.java similarity index 98% rename from src/proguard/classfile/visitor/DotClassClassVisitor.java rename to core/src/proguard/classfile/visitor/DotClassClassVisitor.java index d861d3326..ddbffff8b 100644 --- a/src/proguard/classfile/visitor/DotClassClassVisitor.java +++ b/core/src/proguard/classfile/visitor/DotClassClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/DynamicReturnedClassVisitor.java b/core/src/proguard/classfile/visitor/DynamicReturnedClassVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/DynamicReturnedClassVisitor.java rename to core/src/proguard/classfile/visitor/DynamicReturnedClassVisitor.java index 8a23b02fe..946471e7f 100644 --- a/src/proguard/classfile/visitor/DynamicReturnedClassVisitor.java +++ b/core/src/proguard/classfile/visitor/DynamicReturnedClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ExceptClassFilter.java b/core/src/proguard/classfile/visitor/ExceptClassFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ExceptClassFilter.java rename to core/src/proguard/classfile/visitor/ExceptClassFilter.java index 9182a6f09..15134f9d9 100644 --- a/src/proguard/classfile/visitor/ExceptClassFilter.java +++ b/core/src/proguard/classfile/visitor/ExceptClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ExceptClassesFilter.java b/core/src/proguard/classfile/visitor/ExceptClassesFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ExceptClassesFilter.java rename to core/src/proguard/classfile/visitor/ExceptClassesFilter.java index 35cf7c3e9..101248697 100644 --- a/src/proguard/classfile/visitor/ExceptClassesFilter.java +++ b/core/src/proguard/classfile/visitor/ExceptClassesFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ExceptionCounter.java b/core/src/proguard/classfile/visitor/ExceptionCounter.java similarity index 96% rename from src/proguard/classfile/visitor/ExceptionCounter.java rename to core/src/proguard/classfile/visitor/ExceptionCounter.java index 9a28816ae..8223b2101 100644 --- a/src/proguard/classfile/visitor/ExceptionCounter.java +++ b/core/src/proguard/classfile/visitor/ExceptionCounter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java b/core/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java rename to core/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java index 9f1ed68c1..df4f260af 100644 --- a/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java +++ b/core/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java b/core/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java rename to core/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java index 956b821b0..79ed6ef67 100644 --- a/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java +++ b/core/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ExceptionHandlerFilter.java b/core/src/proguard/classfile/visitor/ExceptionHandlerFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ExceptionHandlerFilter.java rename to core/src/proguard/classfile/visitor/ExceptionHandlerFilter.java index 87cd2ad60..ae56e68c8 100644 --- a/src/proguard/classfile/visitor/ExceptionHandlerFilter.java +++ b/core/src/proguard/classfile/visitor/ExceptionHandlerFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ExceptionOffsetFilter.java b/core/src/proguard/classfile/visitor/ExceptionOffsetFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ExceptionOffsetFilter.java rename to core/src/proguard/classfile/visitor/ExceptionOffsetFilter.java index d4865f256..6ea99be60 100644 --- a/src/proguard/classfile/visitor/ExceptionOffsetFilter.java +++ b/core/src/proguard/classfile/visitor/ExceptionOffsetFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ExceptionRangeFilter.java b/core/src/proguard/classfile/visitor/ExceptionRangeFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ExceptionRangeFilter.java rename to core/src/proguard/classfile/visitor/ExceptionRangeFilter.java index f2e31b6cf..542cf8cf9 100644 --- a/src/proguard/classfile/visitor/ExceptionRangeFilter.java +++ b/core/src/proguard/classfile/visitor/ExceptionRangeFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/visitor/FunctionalInterfaceFilter.java b/core/src/proguard/classfile/visitor/FunctionalInterfaceFilter.java new file mode 100644 index 000000000..6c12858ef --- /dev/null +++ b/core/src/proguard/classfile/visitor/FunctionalInterfaceFilter.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 for functional interfaces, that + * is, interface classes that have exactly one abstract method. + * + * @author Eric Lafortune + */ +public class FunctionalInterfaceFilter implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new ProgramClassFilter. + * @param classVisitor the ClassVisitor to which visits + * will be delegated. + */ + public FunctionalInterfaceFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (isFunctionalInterface(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (isFunctionalInterface(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private boolean isFunctionalInterface(Clazz clazz) + { + // Is it an interface? + if ((clazz.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0) + { + return false; + } + + // Count the methods in the interface hierarchy. + MemberCounter methodCounter = new MemberCounter(); + clazz.hierarchyAccept(true, false, true, false, + new AllMethodVisitor( + new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, + methodCounter))); + + return methodCounter.getCount() == 1; + } +} diff --git a/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java b/core/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ImplementedClassConstantFilter.java rename to core/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java index b9fd44be9..459873864 100644 --- a/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java +++ b/core/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ImplementedClassFilter.java b/core/src/proguard/classfile/visitor/ImplementedClassFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ImplementedClassFilter.java rename to core/src/proguard/classfile/visitor/ImplementedClassFilter.java index 74cbdf4af..5d4107a2d 100644 --- a/src/proguard/classfile/visitor/ImplementedClassFilter.java +++ b/core/src/proguard/classfile/visitor/ImplementedClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java b/core/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ImplementingClassConstantFilter.java rename to core/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java index 471053da5..b9e796c3c 100644 --- a/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java +++ b/core/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/InitializerMethodFilter.java b/core/src/proguard/classfile/visitor/InitializerMethodFilter.java similarity index 98% rename from src/proguard/classfile/visitor/InitializerMethodFilter.java rename to core/src/proguard/classfile/visitor/InitializerMethodFilter.java index 87f363fc6..1c9ccc053 100644 --- a/src/proguard/classfile/visitor/InitializerMethodFilter.java +++ b/core/src/proguard/classfile/visitor/InitializerMethodFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/LibraryClassFilter.java b/core/src/proguard/classfile/visitor/LibraryClassFilter.java similarity index 96% rename from src/proguard/classfile/visitor/LibraryClassFilter.java rename to core/src/proguard/classfile/visitor/LibraryClassFilter.java index f00920659..a92e8bb14 100644 --- a/src/proguard/classfile/visitor/LibraryClassFilter.java +++ b/core/src/proguard/classfile/visitor/LibraryClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/LibraryMemberFilter.java b/core/src/proguard/classfile/visitor/LibraryMemberFilter.java similarity index 97% rename from src/proguard/classfile/visitor/LibraryMemberFilter.java rename to core/src/proguard/classfile/visitor/LibraryMemberFilter.java index 739e26218..cdfc3e9ee 100644 --- a/src/proguard/classfile/visitor/LibraryMemberFilter.java +++ b/core/src/proguard/classfile/visitor/LibraryMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/MemberAccessFilter.java b/core/src/proguard/classfile/visitor/MemberAccessFilter.java similarity index 95% rename from src/proguard/classfile/visitor/MemberAccessFilter.java rename to core/src/proguard/classfile/visitor/MemberAccessFilter.java index 4b049dfb9..c6b463588 100644 --- a/src/proguard/classfile/visitor/MemberAccessFilter.java +++ b/core/src/proguard/classfile/visitor/MemberAccessFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -54,9 +54,9 @@ public class MemberAccessFilter /** * Creates a new MemberAccessFilter. - * @param requiredSetAccessFlags the class access flags that should be + * @param requiredSetAccessFlags the member access flags that should be * set. - * @param requiredUnsetAccessFlags the class access flags that should be + * @param requiredUnsetAccessFlags the member access flags that should be * unset. * @param memberVisitor the MemberVisitor to * which visits will be delegated. diff --git a/src/proguard/optimize/KeepMarker.java b/core/src/proguard/classfile/visitor/MemberAccessFlagCleaner.java similarity index 50% rename from src/proguard/optimize/KeepMarker.java rename to core/src/proguard/classfile/visitor/MemberAccessFlagCleaner.java index c04b5f7df..2784afaeb 100644 --- a/src/proguard/optimize/KeepMarker.java +++ b/core/src/proguard/classfile/visitor/MemberAccessFlagCleaner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -18,86 +18,62 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package proguard.optimize; +package proguard.classfile.visitor; 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. + * This visitor clears the specified access flags of the + * program classes and class members that its visits. + * + * @see ClassConstants * - * @see NoSideEffectMethodMarker * @author Eric Lafortune */ -public class KeepMarker +public class MemberAccessFlagCleaner 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); - } + private final int accessFlags; - public void visitLibraryClass(LibraryClass libraryClass) + /** + * Creates a new MemberAccessFlagCleaner. + * @param accessFlags the member access flags to be cleared. + */ + public MemberAccessFlagCleaner(int accessFlags) { - markAsKept(libraryClass); + this.accessFlags = accessFlags; } - // Implementations for MemberVisitor. - - public void visitProgramField(ProgramClass programClass, ProgramField programField) - { - markAsKept(programField); - } - + // Implementations for ClassVisitor. - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + public void visitProgramClass(ProgramClass programClass) { - markAsKept(MethodLinker.lastMember(programMethod)); + programClass.u2accessFlags &= ~accessFlags; } - - public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + public void visitLibraryClass(LibraryClass libraryClass) { - markAsKept(libraryField); + libraryClass.u2accessFlags &= ~accessFlags; } - public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) - { - markAsKept(MethodLinker.lastMember(libraryMethod)); - } + // Implementations for MemberVisitor. + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} - // Small utility methods. - private static void markAsKept(VisitorAccepter visitorAccepter) + public void visitProgramField(ProgramClass programClass, ProgramField programField) { - visitorAccepter.setVisitorInfo(KEPT); + programField.u2accessFlags &= ~accessFlags; } - public static boolean isKept(VisitorAccepter visitorAccepter) + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - // 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; + programMethod.u2accessFlags &= ~accessFlags; } } diff --git a/core/src/proguard/classfile/visitor/MemberAccessFlagSetter.java b/core/src/proguard/classfile/visitor/MemberAccessFlagSetter.java new file mode 100644 index 000000000..b28c6f7b6 --- /dev/null +++ b/core/src/proguard/classfile/visitor/MemberAccessFlagSetter.java @@ -0,0 +1,66 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 sets the specified access flags of the + * program class members that it visits. + * + * @see ClassConstants + * + * @author Johan Leys + */ +public class MemberAccessFlagSetter +implements MemberVisitor +{ + private final int accessFlags; + + + /** + * Creates a new MemberAccessFlagSetter. + * + * @param accessFlags the member access flags to be set. + */ + public MemberAccessFlagSetter(int accessFlags) + { + this.accessFlags = accessFlags; + } + + + // Implementations for MemberVisitor. + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} + + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + programField.u2accessFlags |= accessFlags; + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + programMethod.u2accessFlags |= accessFlags; + } +} diff --git a/src/proguard/classfile/visitor/MemberClassAccessFilter.java b/core/src/proguard/classfile/visitor/MemberClassAccessFilter.java similarity index 98% rename from src/proguard/classfile/visitor/MemberClassAccessFilter.java rename to core/src/proguard/classfile/visitor/MemberClassAccessFilter.java index 3dcbf951f..acb5e6b1f 100644 --- a/src/proguard/classfile/visitor/MemberClassAccessFilter.java +++ b/core/src/proguard/classfile/visitor/MemberClassAccessFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/visitor/MemberCollector.java b/core/src/proguard/classfile/visitor/MemberCollector.java new file mode 100644 index 000000000..c0b511ec4 --- /dev/null +++ b/core/src/proguard/classfile/visitor/MemberCollector.java @@ -0,0 +1,92 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 dot-separated classname.membername.descriptor + * strings of the class members that it visits. + * + * @author Eric Lafortune + */ +public class MemberCollector +extends SimplifiedVisitor +implements MemberVisitor +{ + private final boolean includeClassName; + private final boolean includeMemberName; + private final boolean includeMemberDescriptor; + + private final Set set; + + + /** + * Creates a new MemberCollector. + * @param includeClassName specifies whether to include the class + * name in each collected strings. + * @param includeMemberName specifies whether to include the member + * name in each collected strings. + * @param includeMemberDescriptor specifies whether to include the member + * descriptor in each collected strings. + * @param set the Set in which all strings will be + * collected. + */ + public MemberCollector(boolean includeClassName, + boolean includeMemberName, + boolean includeMemberDescriptor, + Set set) + { + this.includeClassName = includeClassName; + this.includeMemberName = includeMemberName; + this.includeMemberDescriptor = includeMemberDescriptor; + + this.set = set; + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + StringBuffer buffer = new StringBuffer(); + + if (includeClassName) + { + buffer.append(clazz.getName()).append('.'); + } + + if (includeMemberName) + { + buffer.append(member.getName(clazz)).append('.'); + } + + if (includeMemberDescriptor) + { + buffer.append(member.getDescriptor(clazz)); + } + + set.add(buffer.toString()); + } +} \ No newline at end of file diff --git a/src/proguard/classfile/visitor/MemberCounter.java b/core/src/proguard/classfile/visitor/MemberCounter.java similarity index 97% rename from src/proguard/classfile/visitor/MemberCounter.java rename to core/src/proguard/classfile/visitor/MemberCounter.java index 535bdeb58..0a5dd5eca 100644 --- a/src/proguard/classfile/visitor/MemberCounter.java +++ b/core/src/proguard/classfile/visitor/MemberCounter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/MemberDescriptorFilter.java b/core/src/proguard/classfile/visitor/MemberDescriptorFilter.java similarity index 72% rename from src/proguard/classfile/visitor/MemberDescriptorFilter.java rename to core/src/proguard/classfile/visitor/MemberDescriptorFilter.java index 8d2318b38..d37d08d82 100644 --- a/src/proguard/classfile/visitor/MemberDescriptorFilter.java +++ b/core/src/proguard/classfile/visitor/MemberDescriptorFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,8 @@ import proguard.classfile.*; import proguard.util.*; +import java.util.List; + /** * This MemberVisitor delegates its visits to another given @@ -39,15 +41,34 @@ public class MemberDescriptorFilter implements 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. + * @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(regularExpression, null, memberVisitor); + } + + + /** + * Creates a new MemberDescriptorFilter. + * @param regularExpression the regular expression against which member + * descriptors will be matched. + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. */ public MemberDescriptorFilter(String regularExpression, + List variableStringMatchers, MemberVisitor memberVisitor) { - this(new ClassNameParser().parse(regularExpression), memberVisitor); + this(new ListParser(new ClassNameParser(variableStringMatchers)).parse(regularExpression), + memberVisitor); } diff --git a/src/proguard/classfile/visitor/MemberDescriptorReferencedClassVisitor.java b/core/src/proguard/classfile/visitor/MemberDescriptorReferencedClassVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/MemberDescriptorReferencedClassVisitor.java rename to core/src/proguard/classfile/visitor/MemberDescriptorReferencedClassVisitor.java index 6d7d37749..5a49b1913 100644 --- a/src/proguard/classfile/visitor/MemberDescriptorReferencedClassVisitor.java +++ b/core/src/proguard/classfile/visitor/MemberDescriptorReferencedClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/MemberNameFilter.java b/core/src/proguard/classfile/visitor/MemberNameFilter.java similarity index 73% rename from src/proguard/classfile/visitor/MemberNameFilter.java rename to core/src/proguard/classfile/visitor/MemberNameFilter.java index bca612020..0729aa5f3 100644 --- a/src/proguard/classfile/visitor/MemberNameFilter.java +++ b/core/src/proguard/classfile/visitor/MemberNameFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,8 @@ import proguard.classfile.*; import proguard.util.*; +import java.util.List; + /** * This MemberVisitor delegates its visits to another given @@ -39,19 +41,38 @@ public class MemberNameFilter implements 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. + * @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), + this(regularExpression, null, memberVisitor); + } + + + /** + * Creates a new MemberNameFilter. + * @param regularExpression the regular expression against which member + * names will be matched. + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + * @param memberVisitor the MemberVisitor to which + * visits will be delegated. + */ + public MemberNameFilter(String regularExpression, + List variableStringMatchers, + MemberVisitor memberVisitor) + { + this(new ListParser(new NameParser(variableStringMatchers)).parse(regularExpression), memberVisitor); } + /** /** * Creates a new MemberNameFilter. * @param regularExpressionMatcher the regular expression against which diff --git a/src/proguard/classfile/visitor/MemberToClassVisitor.java b/core/src/proguard/classfile/visitor/MemberToClassVisitor.java similarity index 65% rename from src/proguard/classfile/visitor/MemberToClassVisitor.java rename to core/src/proguard/classfile/visitor/MemberToClassVisitor.java index 4f089f3d0..8c3d0dbcc 100644 --- a/src/proguard/classfile/visitor/MemberToClassVisitor.java +++ b/core/src/proguard/classfile/visitor/MemberToClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,8 +25,7 @@ /** * 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. + * The latter visits the class of each visited class member. * * @author Eric Lafortune */ @@ -34,8 +33,6 @@ public class MemberToClassVisitor implements MemberVisitor { private final ClassVisitor classVisitor; - private Clazz lastVisitedClass; - public MemberToClassVisitor(ClassVisitor classVisitor) { @@ -47,44 +44,24 @@ public MemberToClassVisitor(ClassVisitor classVisitor) public void visitProgramField(ProgramClass programClass, ProgramField programField) { - if (!programClass.equals(lastVisitedClass)) - { - classVisitor.visitProgramClass(programClass); - - lastVisitedClass = programClass; - } + classVisitor.visitProgramClass(programClass); } public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - if (!programClass.equals(lastVisitedClass)) - { - classVisitor.visitProgramClass(programClass); - - lastVisitedClass = programClass; - } + classVisitor.visitProgramClass(programClass); } public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) { - if (!libraryClass.equals(lastVisitedClass)) - { - classVisitor.visitLibraryClass(libraryClass); - - lastVisitedClass = libraryClass; - } + classVisitor.visitLibraryClass(libraryClass); } public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) { - if (!libraryClass.equals(lastVisitedClass)) - { - classVisitor.visitLibraryClass(libraryClass); - - lastVisitedClass = libraryClass; - } + classVisitor.visitLibraryClass(libraryClass); } } diff --git a/src/proguard/classfile/visitor/MemberVisitor.java b/core/src/proguard/classfile/visitor/MemberVisitor.java similarity index 96% rename from src/proguard/classfile/visitor/MemberVisitor.java rename to core/src/proguard/classfile/visitor/MemberVisitor.java index 1e14c9a72..a8564bec9 100644 --- a/src/proguard/classfile/visitor/MemberVisitor.java +++ b/core/src/proguard/classfile/visitor/MemberVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/MemberCollector.java b/core/src/proguard/classfile/visitor/MethodCollector.java similarity index 61% rename from src/proguard/classfile/visitor/MemberCollector.java rename to core/src/proguard/classfile/visitor/MethodCollector.java index 5a344a0c9..72a5cf6ed 100644 --- a/src/proguard/classfile/visitor/MemberCollector.java +++ b/core/src/proguard/classfile/visitor/MethodCollector.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,34 +26,34 @@ import java.util.Set; /** - * This MemberVisitor collects the concatenated name/descriptor strings of - * class members that have been visited. + * This MemberVisitor collects the methods that it visits in the + * given collection. * - * @author Eric Lafortune + * @author Johan Leys */ -public class MemberCollector +public class MethodCollector extends SimplifiedVisitor implements MemberVisitor { - private final Set set; + private final Set methods; - /** - * Creates a new MemberCollector. - * @param set the Set in which all method names/descriptor - * strings will be collected. - */ - public MemberCollector(Set set) + public MethodCollector(Set methods) { - this.set = set; + this.methods = methods; } - // Implementations for MemberVisitor. + // Implementations for MethodCollector. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + methods.add(programMethod); + } - public void visitAnyMember(Clazz clazz, Member member) + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) { - set.add(member.getName(clazz) + member.getDescriptor(clazz)); + methods.add(libraryMethod); } -} \ No newline at end of file +} diff --git a/src/proguard/classfile/visitor/MethodImplementationFilter.java b/core/src/proguard/classfile/visitor/MethodImplementationFilter.java similarity index 97% rename from src/proguard/classfile/visitor/MethodImplementationFilter.java rename to core/src/proguard/classfile/visitor/MethodImplementationFilter.java index 96d0a4e6f..fe401e532 100644 --- a/src/proguard/classfile/visitor/MethodImplementationFilter.java +++ b/core/src/proguard/classfile/visitor/MethodImplementationFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/MethodImplementationTraveler.java b/core/src/proguard/classfile/visitor/MethodImplementationTraveler.java similarity index 98% rename from src/proguard/classfile/visitor/MethodImplementationTraveler.java rename to core/src/proguard/classfile/visitor/MethodImplementationTraveler.java index 1ff7b138d..199cfe100 100644 --- a/src/proguard/classfile/visitor/MethodImplementationTraveler.java +++ b/core/src/proguard/classfile/visitor/MethodImplementationTraveler.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java b/core/src/proguard/classfile/visitor/MultiClassPoolVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/MultiClassPoolVisitor.java rename to core/src/proguard/classfile/visitor/MultiClassPoolVisitor.java index 5fb4cc89b..9bd6de892 100644 --- a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java +++ b/core/src/proguard/classfile/visitor/MultiClassPoolVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/MultiClassVisitor.java b/core/src/proguard/classfile/visitor/MultiClassVisitor.java similarity index 68% rename from src/proguard/classfile/visitor/MultiClassVisitor.java rename to core/src/proguard/classfile/visitor/MultiClassVisitor.java index 379b16a8b..658c42356 100644 --- a/src/proguard/classfile/visitor/MultiClassVisitor.java +++ b/core/src/proguard/classfile/visitor/MultiClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,6 +21,7 @@ package proguard.classfile.visitor; import proguard.classfile.*; +import proguard.util.ArrayUtil; /** @@ -31,18 +32,17 @@ */ public class MultiClassVisitor implements ClassVisitor { - private static final int ARRAY_SIZE_INCREMENT = 5; - private ClassVisitor[] classVisitors; private int classVisitorCount; public MultiClassVisitor() { + this.classVisitors = new ClassVisitor[16]; } - public MultiClassVisitor(ClassVisitor[] classVisitors) + public MultiClassVisitor(ClassVisitor... classVisitors) { this.classVisitors = classVisitors; this.classVisitorCount = classVisitors.length; @@ -51,28 +51,10 @@ public MultiClassVisitor(ClassVisitor[] classVisitors) 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; - } + classVisitors = + ArrayUtil.add(classVisitors, + classVisitorCount++, + classVisitor); } diff --git a/src/proguard/classfile/visitor/MultiMemberVisitor.java b/core/src/proguard/classfile/visitor/MultiMemberVisitor.java similarity index 73% rename from src/proguard/classfile/visitor/MultiMemberVisitor.java rename to core/src/proguard/classfile/visitor/MultiMemberVisitor.java index dde3f3722..7e641dbfa 100644 --- a/src/proguard/classfile/visitor/MultiMemberVisitor.java +++ b/core/src/proguard/classfile/visitor/MultiMemberVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,6 +21,7 @@ package proguard.classfile.visitor; import proguard.classfile.*; +import proguard.util.ArrayUtil; /** @@ -31,18 +32,17 @@ */ public class MultiMemberVisitor implements MemberVisitor { - private static final int ARRAY_SIZE_INCREMENT = 5; - private MemberVisitor[] memberVisitors; private int memberVisitorCount; public MultiMemberVisitor() { + this.memberVisitors = new MemberVisitor[16]; } - public MultiMemberVisitor(MemberVisitor[] memberVisitors) + public MultiMemberVisitor(MemberVisitor... memberVisitors) { this.memberVisitors = memberVisitors; this.memberVisitorCount = memberVisitors.length; @@ -51,28 +51,10 @@ public MultiMemberVisitor(MemberVisitor[] memberVisitors) 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; - } + memberVisitors = + ArrayUtil.add(memberVisitors, + memberVisitorCount++, + memberVisitor); } diff --git a/src/proguard/classfile/visitor/NamedClassVisitor.java b/core/src/proguard/classfile/visitor/NamedClassVisitor.java similarity index 96% rename from src/proguard/classfile/visitor/NamedClassVisitor.java rename to core/src/proguard/classfile/visitor/NamedClassVisitor.java index 2daeba24f..5270e1bb9 100644 --- a/src/proguard/classfile/visitor/NamedClassVisitor.java +++ b/core/src/proguard/classfile/visitor/NamedClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/NamedFieldVisitor.java b/core/src/proguard/classfile/visitor/NamedFieldVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/NamedFieldVisitor.java rename to core/src/proguard/classfile/visitor/NamedFieldVisitor.java index ed8df1496..c6021bc56 100644 --- a/src/proguard/classfile/visitor/NamedFieldVisitor.java +++ b/core/src/proguard/classfile/visitor/NamedFieldVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/NamedMethodVisitor.java b/core/src/proguard/classfile/visitor/NamedMethodVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/NamedMethodVisitor.java rename to core/src/proguard/classfile/visitor/NamedMethodVisitor.java index 817e969b1..f096cf7a3 100644 --- a/src/proguard/classfile/visitor/NamedMethodVisitor.java +++ b/core/src/proguard/classfile/visitor/NamedMethodVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/classfile/visitor/ParallelAllClassVisitor.java b/core/src/proguard/classfile/visitor/ParallelAllClassVisitor.java new file mode 100644 index 000000000..b7be4d198 --- /dev/null +++ b/core/src/proguard/classfile/visitor/ParallelAllClassVisitor.java @@ -0,0 +1,202 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with 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.*; +import java.util.concurrent.*; + + +/** + * This ClassPoolVisitor will visit all Clazz objects of the class pool + * in a parallel way. For each thread, a separate ClassVisitor will be + * created using {@link ClassVisitorFactory#createClassVisitor()}. + *

+ * The number of parallel threads is coupled to the number of available + * processors: + *

+ *     parallel_threads = Runtime.getRuntime().availableProcessors() - 1;
+ * 
+ *

+ * It is possible to override the number of threads by setting the + * environment variable {@code parallel.threads} to an integer > 0. + * + * @author Thomas Neidhart + */ +public class ParallelAllClassVisitor +implements ClassPoolVisitor +{ + private static final int THREAD_COUNT; + static { + Integer threads = null; + try { + String threadCountString = System.getProperty("parallel.threads"); + if (threadCountString != null) + { + threads = Integer.parseInt(threadCountString); + } + } + catch (Exception ex) {} + + threads = threads == null ? + Runtime.getRuntime().availableProcessors() - 1 : + Math.min(threads.intValue(), Runtime.getRuntime().availableProcessors()); + + THREAD_COUNT = threads.intValue(); + } + + + /** + * A factory for ClassVisitor objects. + */ + public interface ClassVisitorFactory + { + /** + * Creates a ClassVisitor that will be used during + * parallel visiting of classes in a ClassPool. + */ + ClassVisitor createClassVisitor(); + } + + + private final ClassVisitorFactory classVisitorFactory; + + + /** + * Create a new ParallelAllClassVisitor that will use the given factory + * to visit all classes in a ClassPool in a parallel way. + */ + public ParallelAllClassVisitor(ClassVisitorFactory classVisitorFactory) + { + this.classVisitorFactory = classVisitorFactory; + } + + + // Implementations for ClassPoolVisitor. + + public void visitClassPool(ClassPool classPool) + { + if (THREAD_COUNT <= 1) + { + // Fallback to single thread execution if the thread count + // was overridden by an environment variable. + classPool.classesAccept(classVisitorFactory.createClassVisitor()); + } + else + { + ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT, new MyThreadFactory()); + + MyThreadedClassVisitor classVisitor = new MyThreadedClassVisitor(executor); + + classPool.classesAccept(classVisitor); + + try + { + // Shutdown the executor service to release memory. + executor.shutdown(); + + // Rethrow any exception that was thrown in the executor threads. + classVisitor.awaitTermination(); + } + catch (InterruptedException e) + { + throw new RuntimeException("Parallel execution is taking too long", e); + } + catch (ExecutionException e) + { + throw new RuntimeException(e.getCause()); + } + } + } + + + private class MyThreadFactory + implements ThreadFactory + { + private int threadCounter = 0; + + public Thread newThread(Runnable runnable) + { + return new MyClassVisitorThread(++threadCounter, runnable); + } + } + + + private class MyClassVisitorThread + extends Thread + { + private final ClassVisitor classVisitor = classVisitorFactory.createClassVisitor(); + + public MyClassVisitorThread(int counter, Runnable runnable) + { + super(runnable, "Parallel Class Visitor " + counter); + } + } + + + private static class MyThreadedClassVisitor + implements ClassVisitor + { + private final ExecutorService executorService; + + private final List futures = new ArrayList(); + + public MyThreadedClassVisitor(ExecutorService executorService) + { + this.executorService = executorService; + } + + public void awaitTermination() throws ExecutionException, InterruptedException + { + for (Future future : futures) + { + future.get(); + } + } + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) + { + submitClassToExecutorService(libraryClass); + } + + + public void visitProgramClass(ProgramClass programClass) + { + submitClassToExecutorService(programClass); + } + + + private void submitClassToExecutorService(final Clazz clazz) + { + futures.add(executorService.submit(new Runnable() + { + public void run() + { + MyClassVisitorThread thread = (MyClassVisitorThread)Thread.currentThread(); + clazz.accept(thread.classVisitor); + } + })); + } + } +} diff --git a/src/proguard/classfile/visitor/ParameterVisitor.java b/core/src/proguard/classfile/visitor/ParameterVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/ParameterVisitor.java rename to core/src/proguard/classfile/visitor/ParameterVisitor.java index 805e1de08..503d1ac86 100644 --- a/src/proguard/classfile/visitor/ParameterVisitor.java +++ b/core/src/proguard/classfile/visitor/ParameterVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ProgramClassFilter.java b/core/src/proguard/classfile/visitor/ProgramClassFilter.java similarity index 96% rename from src/proguard/classfile/visitor/ProgramClassFilter.java rename to core/src/proguard/classfile/visitor/ProgramClassFilter.java index 088da872e..0d57634da 100644 --- a/src/proguard/classfile/visitor/ProgramClassFilter.java +++ b/core/src/proguard/classfile/visitor/ProgramClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ProgramMemberFilter.java b/core/src/proguard/classfile/visitor/ProgramMemberFilter.java similarity index 97% rename from src/proguard/classfile/visitor/ProgramMemberFilter.java rename to core/src/proguard/classfile/visitor/ProgramMemberFilter.java index 25299f429..9315b373a 100644 --- a/src/proguard/classfile/visitor/ProgramMemberFilter.java +++ b/core/src/proguard/classfile/visitor/ProgramMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ReferencedClassVisitor.java b/core/src/proguard/classfile/visitor/ReferencedClassVisitor.java similarity index 99% rename from src/proguard/classfile/visitor/ReferencedClassVisitor.java rename to core/src/proguard/classfile/visitor/ReferencedClassVisitor.java index f60c06da4..9b62ff684 100644 --- a/src/proguard/classfile/visitor/ReferencedClassVisitor.java +++ b/core/src/proguard/classfile/visitor/ReferencedClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/ReferencedMemberVisitor.java b/core/src/proguard/classfile/visitor/ReferencedMemberVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/ReferencedMemberVisitor.java rename to core/src/proguard/classfile/visitor/ReferencedMemberVisitor.java index 768bdf49e..81cd166aa 100644 --- a/src/proguard/classfile/visitor/ReferencedMemberVisitor.java +++ b/core/src/proguard/classfile/visitor/ReferencedMemberVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/SimilarMemberVisitor.java b/core/src/proguard/classfile/visitor/SimilarMemberVisitor.java similarity index 99% rename from src/proguard/classfile/visitor/SimilarMemberVisitor.java rename to core/src/proguard/classfile/visitor/SimilarMemberVisitor.java index 21abb2f94..6d6c689b2 100644 --- a/src/proguard/classfile/visitor/SimilarMemberVisitor.java +++ b/core/src/proguard/classfile/visitor/SimilarMemberVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/SimpleClassPrinter.java b/core/src/proguard/classfile/visitor/SimpleClassPrinter.java similarity index 82% rename from src/proguard/classfile/visitor/SimpleClassPrinter.java rename to core/src/proguard/classfile/visitor/SimpleClassPrinter.java index 9ee92fc11..34fa6ef51 100644 --- a/src/proguard/classfile/visitor/SimpleClassPrinter.java +++ b/core/src/proguard/classfile/visitor/SimpleClassPrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,7 +23,7 @@ import proguard.classfile.*; import proguard.classfile.util.ClassUtil; -import java.io.PrintStream; +import java.io.PrintWriter; /** @@ -39,36 +39,30 @@ public class SimpleClassPrinter MemberVisitor { private final boolean printAccessModifiers; - private final PrintStream ps; + private final PrintWriter pw; /** - * 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. + * Creates a new SimpleClassPrinter that prints to the standard output, with + * or without the access modifiers. */ public SimpleClassPrinter(boolean printAccessModifiers) { - this(printAccessModifiers, System.out); + // We're using the system's default character encoding for writing to + // the standard output. + this(printAccessModifiers, new PrintWriter(System.out, true)); } + /** - * Creates a new SimpleClassPrinter that prints to the given - * PrintStream, with or without the access modifiers. + * Creates a new SimpleClassPrinter that prints to the given writer, with + * or without the access modifiers. */ public SimpleClassPrinter(boolean printAccessModifiers, - PrintStream printStream) + PrintWriter printWriter) { this.printAccessModifiers = printAccessModifiers; - this.ps = printStream; + this.pw = printWriter; } @@ -76,7 +70,7 @@ public SimpleClassPrinter(boolean printAccessModifiers, public void visitProgramClass(ProgramClass programClass) { - ps.println(ClassUtil.externalFullClassDescription( + pw.println(ClassUtil.externalFullClassDescription( printAccessModifiers ? programClass.getAccessFlags() : 0, @@ -86,7 +80,7 @@ public void visitProgramClass(ProgramClass programClass) public void visitLibraryClass(LibraryClass libraryClass) { - ps.println(ClassUtil.externalFullClassDescription( + pw.println(ClassUtil.externalFullClassDescription( printAccessModifiers ? libraryClass.getAccessFlags() : 0, @@ -98,7 +92,7 @@ public void visitLibraryClass(LibraryClass libraryClass) public void visitProgramField(ProgramClass programClass, ProgramField programField) { - ps.println(ClassUtil.externalFullClassDescription( + pw.println(ClassUtil.externalFullClassDescription( printAccessModifiers ? programClass.getAccessFlags() : 0, @@ -115,7 +109,7 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - ps.println(ClassUtil.externalFullClassDescription( + pw.println(ClassUtil.externalFullClassDescription( printAccessModifiers ? programClass.getAccessFlags() : 0, @@ -133,7 +127,7 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) { - ps.println(ClassUtil.externalFullClassDescription( + pw.println(ClassUtil.externalFullClassDescription( printAccessModifiers ? libraryClass.getAccessFlags() : 0, @@ -150,7 +144,7 @@ public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryFie public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) { - ps.println(ClassUtil.externalFullClassDescription( + pw.println(ClassUtil.externalFullClassDescription( printAccessModifiers ? libraryClass.getAccessFlags() : 0, diff --git a/src/proguard/optimize/KeptClassFilter.java b/core/src/proguard/classfile/visitor/SingleTimeClassVisitor.java similarity index 67% rename from src/proguard/optimize/KeptClassFilter.java rename to core/src/proguard/classfile/visitor/SingleTimeClassVisitor.java index e1c7f0890..2d6ef80a0 100644 --- a/src/proguard/optimize/KeptClassFilter.java +++ b/core/src/proguard/classfile/visitor/SingleTimeClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -18,31 +18,24 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package proguard.optimize; +package proguard.classfile.visitor; 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 + * This ClassVisitor delegates all visits to a given ClassVisitor, although + * only once to the same class in a row. * * @author Eric Lafortune */ -public class KeptClassFilter -implements ClassVisitor +public class SingleTimeClassVisitor implements ClassVisitor { private final ClassVisitor classVisitor; + private Clazz lastVisitedClass; + - /** - * Creates a new KeptClassFilter. - * @param classVisitor the class visitor to which the visiting will be - * delegated. - */ - public KeptClassFilter(ClassVisitor classVisitor) + public SingleTimeClassVisitor(ClassVisitor classVisitor) { this.classVisitor = classVisitor; } @@ -52,18 +45,22 @@ public KeptClassFilter(ClassVisitor classVisitor) public void visitProgramClass(ProgramClass programClass) { - if (KeepMarker.isKept(programClass)) + if (!programClass.equals(lastVisitedClass)) { classVisitor.visitProgramClass(programClass); + + lastVisitedClass = programClass; } } public void visitLibraryClass(LibraryClass libraryClass) { - if (KeepMarker.isKept(libraryClass)) + if (!libraryClass.equals(lastVisitedClass)) { classVisitor.visitLibraryClass(libraryClass); + + lastVisitedClass = libraryClass; } } -} \ No newline at end of file +} diff --git a/src/proguard/classfile/visitor/SubclassFilter.java b/core/src/proguard/classfile/visitor/SubclassFilter.java similarity index 97% rename from src/proguard/classfile/visitor/SubclassFilter.java rename to core/src/proguard/classfile/visitor/SubclassFilter.java index 3ae644f6c..ea377fba6 100644 --- a/src/proguard/classfile/visitor/SubclassFilter.java +++ b/core/src/proguard/classfile/visitor/SubclassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/SubclassTraveler.java b/core/src/proguard/classfile/visitor/SubclassTraveler.java similarity index 96% rename from src/proguard/classfile/visitor/SubclassTraveler.java rename to core/src/proguard/classfile/visitor/SubclassTraveler.java index e3ee6b72e..13c3cd986 100644 --- a/src/proguard/classfile/visitor/SubclassTraveler.java +++ b/core/src/proguard/classfile/visitor/SubclassTraveler.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/VariableClassVisitor.java b/core/src/proguard/classfile/visitor/VariableClassVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/VariableClassVisitor.java rename to core/src/proguard/classfile/visitor/VariableClassVisitor.java index 31dc216b8..9d72f5a57 100644 --- a/src/proguard/classfile/visitor/VariableClassVisitor.java +++ b/core/src/proguard/classfile/visitor/VariableClassVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/VariableMemberVisitor.java b/core/src/proguard/classfile/visitor/VariableMemberVisitor.java similarity index 97% rename from src/proguard/classfile/visitor/VariableMemberVisitor.java rename to core/src/proguard/classfile/visitor/VariableMemberVisitor.java index 91d6b060c..626804167 100644 --- a/src/proguard/classfile/visitor/VariableMemberVisitor.java +++ b/core/src/proguard/classfile/visitor/VariableMemberVisitor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/classfile/visitor/package.html b/core/src/proguard/classfile/visitor/package.html similarity index 100% rename from src/proguard/classfile/visitor/package.html rename to core/src/proguard/classfile/visitor/package.html diff --git a/core/src/proguard/configuration/ConfigurationLogger.java b/core/src/proguard/configuration/ConfigurationLogger.java new file mode 100644 index 000000000..0b080152e --- /dev/null +++ b/core/src/proguard/configuration/ConfigurationLogger.java @@ -0,0 +1,789 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.configuration; + + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +/** + * This class can be injected in applications to log information about reflection + * being used in the application code, and suggest appropriate ProGuard rules for + * keeping the reflected classes, methods and/or fields. + * + * @author Johan Leys + */ +public class ConfigurationLogger implements Runnable +{ + public static final boolean LOG_ONCE = true; + + private static final String LOG_TAG = "ProGuard"; + + public static final String CLASS_MAP_FILENAME = "classmap.txt"; + + private static final String EMPTY_LINE = "\u00a0\n"; + + // Set with missing class names. + private static final Set missingClasses = new HashSet(); + + // Map from class name to missing constructors. + private static final Map> missingConstructors = new HashMap>(); + // Set of classes on which getConstructors or getDeclaredConstructors is invoked. + private static final Set constructorListingClasses = new HashSet(); + + // Map from class name to missing method signatures. + private static final Map> missingMethods = new HashMap>(); + // Set of classes on which getMethods or getDeclaredMethods is invoked. + private static final Set methodListingClasses = new HashSet(); + + // Map from class name to missing field names. + private static final Map> missingFields = new HashMap>(); + // Set of classes on which getFields or getDeclaredFields is invoked. + private static final Set fieldListingCLasses = new HashSet(); + + // Map from obfuscated class name to original class name. + private static Map classNameMap; + + // Set of classes that have renamed or removed methods. + private static Set classesWithObfuscatedMethods; + + // Set of classes that have renamed or removed fields. + private static Set classesWithObfuscatedFields; + + private static Method logMethod; + + // Try to find the Android logging class. + static + { + try + { + Class logClass = Class.forName("android.util.Log"); + logMethod = logClass.getMethod("w", String.class, String. class); + } + catch (Exception e) {} + } + + // Classes. + + /** + * Log a failed call to Class.forName(). + * + * @param callingClassName + * @param missingClassName + */ + public static void logForName(String callingClassName, + String missingClassName) + { + logMissingClass(callingClassName, "Class", "forName", missingClassName); + } + + /** + * Log a failed call to ClassLoader.loadClass(). + * + * @param callingClassName + * @param missingClassName + */ + public static void logLoadClass(String callingClassName, + String missingClassName) + { + logMissingClass(callingClassName, "ClassLoader", "loadClass", missingClassName); + } + + + /** + * Log a failed call to Class.forName(). + * + * @param callingClassName + * @param missingClassName + */ + public static void logMissingClass(String callingClassName, + String invokedClassName, + String invokedMethodName, + String missingClassName) + { + if (!LOG_ONCE || !missingClasses.contains(missingClassName)) + { + missingClasses.add(missingClassName); + log( + "The class '" + originalClassName(callingClassName) + "' is calling " + invokedClassName + "." + invokedMethodName + " to retrieve\n" + + "the class '" + missingClassName + "', but the latter could not be found.\n" + + "It may have been obfuscated or shrunk.\n" + + "You should consider preserving the class with its original name,\n" + + "with a setting like:\n" + + EMPTY_LINE + + keepClassRule(missingClassName) + "\n" + + EMPTY_LINE); + } + } + + + // Constructors. + + + /** + * Log a failed call to Class.getDeclaredConstructor(). + * + * @param invokingClassName + * @param reflectedClass + * @param constructorParameters + */ + public static void logGetDeclaredConstructor(String invokingClassName, + Class reflectedClass, + Class[] constructorParameters) + { + logGetConstructor(invokingClassName, "getDeclaredConstructor", reflectedClass, constructorParameters); + } + + + /** + * Log a failed call to Class.getConstructor(). + * + * @param invokingClassName + * @param reflectedClass + * @param constructorParameters + */ + public static void logGetConstructor(String invokingClassName, + Class reflectedClass, + Class[] constructorParameters) + { + logGetConstructor(invokingClassName, "getConstructor", reflectedClass, constructorParameters); + } + + + /** + * Log a failed call to one of the constructor retrieving methods on Class. + * + * @param invokingClassName + * @param invokedMethodName + * @param reflectedClass + * @param constructorParameters + */ + public static void logGetConstructor(String invokingClassName, + String invokedMethodName, + Class reflectedClass, + Class[] constructorParameters) + { + MethodSignature signature = new MethodSignature("", constructorParameters); + + Set constructors = missingConstructors.get(reflectedClass.getName()); + if (constructors == null) + { + constructors = new HashSet(); + missingConstructors.put(reflectedClass.getName(), constructors); + } + + if ((!LOG_ONCE || !constructors.contains(signature)) && !isLibraryClass(reflectedClass)) + { + constructors.add(signature); + log( + "The class '" + originalClassName(invokingClassName) + "' is calling Class." + invokedMethodName + "\n" + + "on class '" + originalClassName(reflectedClass) + "' to retrieve\n" + + "the constructor with signature (" + originalSignature(signature) + "), but the latter could not be found.\n" + + "It may have been obfuscated or shrunk.\n" + + "You should consider preserving the constructor, with a setting like:\n" + + EMPTY_LINE + + keepConstructorRule(reflectedClass.getName(), signature) + "\n" + + EMPTY_LINE); + } + } + + + /** + * Log a call to Class.getDeclaredConstructors(). + * + * @param invokingClassName + * @param reflectedClass + */ + public static void logGetDeclaredConstructors(String invokingClassName, + Class reflectedClass ) + { + logGetConstructors(invokingClassName, reflectedClass, "getDeclaredConstructors"); + } + + + /** + * Log a call to Class.getConstructors(). + * + * @param invokingClassName + * @param reflectedClass + */ + public static void logGetConstructors(String invokingClassName, + Class reflectedClass ) + { + logGetConstructors(invokingClassName, reflectedClass, "getConstructors"); + } + + + /** + * Log a call to one of the constructor listing methods on Class. + * + * @param invokingClassName + * @param reflectedClass + * @param reflectedMethodName + */ + private static void logGetConstructors(String invokingClassName, + Class reflectedClass, + String reflectedMethodName) + { + initializeMappings(); + if (classesWithObfuscatedMethods.contains(reflectedClass.getName()) && + !constructorListingClasses.contains(reflectedClass.getName()) && + !isLibraryClass(reflectedClass)) + { + constructorListingClasses.add(reflectedClass.getName()); + log( + "The class '" + originalClassName(invokingClassName) + "' is calling Class." + reflectedMethodName + "\n" + + "on class '" + originalClassName(reflectedClass) + "' to retrieve its constructors.\n" + + "You might consider preserving all constructors with their original names,\n" + + "with a setting like:\n" + + EMPTY_LINE + + keepAllConstructorsRule(reflectedClass) + "\n" + + EMPTY_LINE); + } + } + + + // Methods. + + + /** + * Log a failed call to Class.getDeclaredMethod(). + * + * @param invokingClassName + * @param reflectedClass + * @param reflectedMethodName + * @param methodParameters + */ + public static void logGetDeclaredMethod(String invokingClassName, + Class reflectedClass, + String reflectedMethodName, + Class[] methodParameters ) + { + logGetMethod(invokingClassName, "getDeclaredMethod", reflectedClass, reflectedMethodName, methodParameters); + } + + + /** + * Log a failed call to Class.getMethod(). + * + * @param invokingClassName + * @param reflectedClass + * @param reflectedMethodName + * @param methodParameters + */ + public static void logGetMethod(String invokingClassName, + Class reflectedClass, + String reflectedMethodName, + Class[] methodParameters ) + { + logGetMethod(invokingClassName, "getMethod", reflectedClass, reflectedMethodName, methodParameters); + } + + + /** + * Log a failed call to one of the method retrieving methods on Class. + * @param invokingClassName + * @param invokedReflectionMethodName + * @param reflectedClass + * @param reflectedMethodName + * @param methodParameters + */ + private static void logGetMethod(String invokingClassName, + String invokedReflectionMethodName, + Class reflectedClass, + String reflectedMethodName, + Class[] methodParameters ) + { + Set methods = missingMethods.get(reflectedClass.getName()); + if (methods == null) + { + methods = new HashSet(); + missingMethods.put(reflectedClass.getName(), methods); + } + + MethodSignature signature = new MethodSignature(reflectedMethodName, methodParameters); + if (!methods.contains(signature) && !isLibraryClass(reflectedClass)) + { + methods.add(signature); + log( + "The class '" + originalClassName(invokingClassName) + + "' is calling Class." + invokedReflectionMethodName + "\n" + + "on class '" + originalClassName(reflectedClass) + + "' to retrieve the method\n" + + reflectedMethodName + "(" + originalSignature(signature) + "),\n" + + "but the latter could not be found. It may have been obfuscated or shrunk.\n" + + "You should consider preserving the method with its original name,\n" + + "with a setting like:\n" + + EMPTY_LINE + + keepMethodRule(reflectedClass.getName(), reflectedMethodName, signature) + "\n" + + EMPTY_LINE); + } + } + + + /** + * Log a call to Class.getDeclaredMethods(). + * + * @param invokingClassName + * @param reflectedClass + */ + public static void logGetDeclaredMethods(String invokingClassName, + Class reflectedClass ) + { + logGetMethods(invokingClassName, "getDeclaredMethods", reflectedClass); + } + + + /** + * Log a call to Class.getMethods(). + * + * @param invokingClassName + * @param reflectedClass + */ + public static void logGetMethods(String invokingClassName, + Class reflectedClass ) + { + logGetMethods(invokingClassName, "getMethods", reflectedClass); + } + + + /** + * Log a call to one of the method listing methods on Class. + * + * @param invokingClassName + * @param invokedReflectionMethodName + * @param reflectedClass + */ + private static void logGetMethods(String invokingClassName, + String invokedReflectionMethodName, + Class reflectedClass ) + { + initializeMappings(); + if (classesWithObfuscatedMethods.contains(reflectedClass.getName()) && + !methodListingClasses.contains(reflectedClass.getName()) && + !isLibraryClass(reflectedClass)) + { + methodListingClasses.add(reflectedClass.getName()); + log( + "The class '" + originalClassName(invokingClassName) + + "' is calling Class." + invokedReflectionMethodName + "\n" + + "on class '" + originalClassName(reflectedClass) + + "' to retrieve its methods.\n" + + "You might consider preserving all methods with their original names,\n" + + "with a setting like:\n" + + EMPTY_LINE + + keepAllMethodsRule(reflectedClass) + "\n" + + EMPTY_LINE); + } + } + + + // Fields. + + + /** + * Log a failed call to Class.getField(). + * + * @param invokingClassName + * @param reflectedClass + * @param reflectedFieldName + */ + public static void logGetField(String invokingClassName, + Class reflectedClass, + String reflectedFieldName) + { + logGetField(invokingClassName, "getField", reflectedClass, reflectedFieldName); + } + + + /** + * Log a failed call to Class.getDeclaredField(). + * + * @param invokingClassName + * @param reflectedClass + * @param reflectedFieldName + */ + public static void logGetDeclaredField(String invokingClassName, + Class reflectedClass, + String reflectedFieldName) + { + logGetField(invokingClassName, "getDeclaredField", reflectedClass, reflectedFieldName); + } + + + /** + * Log a failed call to one of the field retrieving methods of Class. + * + * @param invokingClassName + * @param invokedReflectionMethodName + * @param reflectedClass + * @param reflectedFieldName + */ + private static void logGetField(String invokingClassName, + String invokedReflectionMethodName, + Class reflectedClass, + String reflectedFieldName ) + { + Set fields = missingFields.get(reflectedClass.getName()); + if (fields == null) + { + fields = new HashSet(); + missingFields.put(reflectedClass.getName(), fields); + } + + if ((!LOG_ONCE || !fields.contains(reflectedFieldName)) && + !isLibraryClass(reflectedClass)) + { + fields.add(reflectedFieldName); + log( + "The class '" + originalClassName(invokingClassName) + + "' is calling Class." + invokedReflectionMethodName + "\n" + + "on class '" + originalClassName(reflectedClass) + + "' to retrieve the field '" + reflectedFieldName + "',\n" + + "but the latter could not be found. It may have been obfuscated or shrunk.\n" + + "You should consider preserving the field with its original name,\n" + + "with a setting like:\n" + + EMPTY_LINE + + keepFieldRule(reflectedClass.getName(), reflectedFieldName) + "\n" + + EMPTY_LINE); + } + } + + + /** + * Log a call to Class.getDeclaredFields(). + * + * @param invokingClassName + * @param reflectedClass + */ + public static void logGetDeclaredFields(String invokingClassName, + Class reflectedClass ) + { + logGetFields(invokingClassName, "getDeclaredFields", reflectedClass); + } + + + /** + * Log a call to Class.getFields(). + * + * @param invokingClassName + * @param reflectedClass + */ + public static void logGetFields(String invokingClassName, + Class reflectedClass ) + { + logGetFields(invokingClassName, "getFields", reflectedClass); + } + + + /** + * Log a call to one of the field listing methods on Class. + * + * @param invokingClassName + * @param invokedReflectionMethodName + * @param reflectedClass + */ + private static void logGetFields(String invokingClassName, + String invokedReflectionMethodName, + Class reflectedClass ) + { + initializeMappings(); + if (classesWithObfuscatedFields.contains(reflectedClass.getName()) && + !fieldListingCLasses.contains(reflectedClass.getName()) && + !isLibraryClass(reflectedClass)) + { + fieldListingCLasses.add(reflectedClass.getName()); + log( + "The class '" + originalClassName(invokingClassName) + + "' is calling Class." + invokedReflectionMethodName + "\n" + + "on class '" + originalClassName(reflectedClass) + + "' to retrieve its fields.\n" + + "You might consider preserving all fields with their original names,\n" + + "with a setting like:\n" + + EMPTY_LINE + + keepAllFieldsRule(reflectedClass) + "\n" + + EMPTY_LINE); + } + } + + + // Implementations for Runnable. + + public void run() + { + printConfiguration(); + } + + + private static void printConfiguration() + { + log("The following settings may help solving issues related to\n" + + "missing classes, methods and/or fields:\n"); + + for (String clazz : missingClasses) + { + log(keepClassRule(clazz) + "\n"); + } + + for (String clazz : missingConstructors.keySet()) + { + for (MethodSignature constructor : missingConstructors.get(clazz)) + { + log(keepConstructorRule(clazz, constructor) + "\n"); + } + } + + for (String clazz : missingMethods.keySet()) + { + for (MethodSignature method : missingMethods.get(clazz)) + { + log(keepMethodRule(clazz, method.name, method) + "\n"); + } + } + + for (String clazz : missingFields.keySet()) + { + for (String field : missingFields.get(clazz)) + { + log(keepFieldRule(clazz, field) + "\n"); + } + } + } + + + // ProGuard rules. + + private static String keepClassRule(String className) + { + return "-keep class " + className; + } + + + private static String keepConstructorRule(String className, + MethodSignature constructorParameters) + { + return "-keepclassmembers class " + originalClassName(className) + " {\n" + + " public (" + originalSignature(constructorParameters) + ");\n" + + "}"; + } + + + private static String keepMethodRule(String className, + String methodName, + MethodSignature constructorParameters) + { + return "-keepclassmembers class " + originalClassName(className) + " {\n" + + " *** " + methodName + "(" + originalSignature(constructorParameters) + ");\n" + + "}"; + } + + + private static String keepFieldRule(String className, + String fieldName) + { + return "-keepclassmembers class " + originalClassName(className) + " {\n" + + " *** " + fieldName + ";\n" + + "}"; + } + + + private static String keepAllConstructorsRule(Class className) + { + return "-keepclassmembers class " + originalClassName(className) + " {\n" + + " (...);\n" + + "}"; + } + + + private static String keepAllMethodsRule(Class className) + { + return "-keepclassmembers class " + originalClassName(className) + " {\n" + + " ;\n" + + "}"; + } + + + private static String keepAllFieldsRule(Class className) + { + return "-keepclassmembers class " + originalClassName(className) + " {\n" + + " ;\n" + + "}"; + } + + + private static String originalClassName(Class className) + { + return originalClassName(className.getName()); + } + + + private static String originalClassName(String className) + { + initializeMappings(); + String originalClassName = classNameMap.get(className); + return originalClassName != null ? originalClassName : className; + } + + + /** + * Simple heuristic to see if the given class is a library class or not. + * + * @param clazz + * @return + */ + private static boolean isLibraryClass(Class clazz) + { + return clazz.getClassLoader() == String.class.getClassLoader(); + } + + + /** + * Log a message, either on the Android Logcat, if available, or on the + * Standard error outputstream otherwise. + * + * @param message the message to be logged. + */ + private static void log(String message) + { + if (logMethod != null) + { + try + { + logMethod.invoke(null, LOG_TAG, message); + } + catch (Exception e) + { + System.err.println(message); + } + } + else + { + System.err.println(message); + } + } + + + private static void initializeMappings() + { + if (classNameMap == null) + { + classNameMap = new HashMap (); + classesWithObfuscatedMethods = new HashSet (); + classesWithObfuscatedFields = new HashSet (); + + String line; + try + { + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + ConfigurationLogger.class.getClassLoader().getResourceAsStream(CLASS_MAP_FILENAME))); + + while ((line = reader.readLine()) != null) + { + StringTokenizer tokenizer = new StringTokenizer(line, ","); + String originalClassName = tokenizer.nextToken(); + String obfuscatedClassName = tokenizer.nextToken(); + boolean hasObfuscatedMethods = tokenizer.nextToken().equals("1"); + boolean hasObfuscatedFields = tokenizer.nextToken().equals("1"); + + classNameMap.put(obfuscatedClassName, originalClassName); + + if (hasObfuscatedMethods) + { + classesWithObfuscatedMethods.add(obfuscatedClassName); + } + + if (hasObfuscatedFields) + { + classesWithObfuscatedFields.add(obfuscatedClassName); + } + } + reader.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + + + private static String originalSignature(MethodSignature signature) + { + StringBuilder stringBuilder = new StringBuilder(); + boolean first = true; + for (String clazz : signature.parameters) + { + if (first) + { + first = false; + } + else + { + stringBuilder.append(","); + } + stringBuilder.append(originalClassName(clazz)); + } + return stringBuilder.toString(); + } + + + public static class MethodSignature + { + private String name; + private String[] parameters; + + + public MethodSignature(String name, Class[] parameters) + { + this.name = name; + this.parameters = new String[parameters.length]; + for (int i = 0; i < parameters.length; i++) + { + this.parameters[i] = parameters[i].getName(); + } + } + + + // Implementations for Object. + + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MethodSignature that = (MethodSignature)o; + + if (!name.equals(that.name)) return false; + return Arrays.equals(parameters, that.parameters); + } + + + public int hashCode() + { + int result = name.hashCode(); + result = 31 * result + Arrays.hashCode(parameters); + return result; + } + } +} diff --git a/core/src/proguard/configuration/ConfigurationLoggingAdder.java b/core/src/proguard/configuration/ConfigurationLoggingAdder.java new file mode 100644 index 000000000..e8999dff0 --- /dev/null +++ b/core/src/proguard/configuration/ConfigurationLoggingAdder.java @@ -0,0 +1,129 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.configuration; + + +import proguard.*; +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.io.*; +import proguard.optimize.peephole.*; +import proguard.util.MultiValueMap; + +import java.io.IOException; + +import static proguard.classfile.util.ClassUtil.internalClassName; + +/** + * This class can add configuration debug logging code to all code that + * relies on reflection. The added code prints suggestions on which keep + * rules to add to ensure the reflection code will continue working after + * obfuscation and shrinking. + * + * @author Johan Leys + */ +public class ConfigurationLoggingAdder +extends SimplifiedVisitor +implements // Implementation interfaces. + InstructionVisitor +{ + private final Configuration configuration; + + // Field acting as parameter for the visitor methods. + private MultiValueMap injectedClassMap; + + + /** + * Creates a new ConfigurationLoggingAdder. + */ + public ConfigurationLoggingAdder(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Instrumets the given program class pool. + */ + public void execute(ClassPool programClassPool, + ClassPool libraryClassPool, + MultiValueMap injectedClassMap ) + { + // Load the logging utility classes in the program class pool. + // TODO: The initialization could be incomplete if the loaded classes depend on one another. + ClassReader classReader = + new ClassReader(false, false, false, null, + new MultiClassVisitor( + new ClassPoolFiller(programClassPool), + new ClassReferenceInitializer(programClassPool, libraryClassPool), + new ClassSubHierarchyInitializer() + )); + + try + { + classReader.read(new ClassPathDataEntry(ConfigurationLogger.MethodSignature.class)); + classReader.read(new ClassPathDataEntry(ConfigurationLogger.class)); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + + // Set up the instruction sequences and their replacements. + ConfigurationLoggingInstructionSequenceConstants constants = + new ConfigurationLoggingInstructionSequenceConstants(programClassPool, + libraryClassPool); + + BranchTargetFinder branchTargetFinder = new BranchTargetFinder(); + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + // Set the injected class map for the extra visitor. + this.injectedClassMap = injectedClassMap; + + // Replace the instruction sequences in all non-ProGuard classes. + programClassPool.classesAccept( + new ClassNameFilter("!proguard/**", + new AllMethodVisitor( + new AllAttributeVisitor( + new PeepholeOptimizer(branchTargetFinder, codeAttributeEditor, + new ConfigurationLoggingInstructionSequencesReplacer(constants.CONSTANTS, + constants.RESOURCE, + branchTargetFinder, + codeAttributeEditor, + this)))))); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Add a dependency from the modified class on the logging class. + injectedClassMap.put(clazz.getName(), internalClassName(ConfigurationLogger.class.getName())); + injectedClassMap.put(clazz.getName(), internalClassName(ConfigurationLogger.MethodSignature.class.getName())); + } +} diff --git a/core/src/proguard/configuration/ConfigurationLoggingInstructionSequenceConstants.java b/core/src/proguard/configuration/ConfigurationLoggingInstructionSequenceConstants.java new file mode 100644 index 000000000..36f353fe3 --- /dev/null +++ b/core/src/proguard/configuration/ConfigurationLoggingInstructionSequenceConstants.java @@ -0,0 +1,479 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.configuration; + +import proguard.classfile.*; +import proguard.classfile.constant.Constant; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.util.*; +import proguard.optimize.peephole.InstructionSequenceReplacer; + +import static proguard.optimize.peephole.InstructionSequenceReplacer.catch_; + +/** + * This class contains a set of instruction sequences for accessing class information via reflection, and replacement instructions that add logging information on the reflection that is used. + * + * @author Johan Leys + */ +public class ConfigurationLoggingInstructionSequenceConstants +{ + private static String LOGGER_CLASS_NAME = ClassUtil.internalClassName(ConfigurationLogger.class.getName()); + + // Exceptions classes. + public static final String NAME_CLASS_NOT_FOUND_EXCEPTION = "java/lang/ClassNotFoundException"; + public static final String NAME_NO_SUCH_FIELD_EXCEPTION = "java/lang/NoSuchFieldException"; + public static final String NAME_NO_SUCH_METHOD_EXCEPTION = "java/lang/NoSuchMethodException"; + public static final String NAME_RUNTIME_EXCEPTION = "java/lang/RuntimeException"; + public static final String NAME_UNSATISFIED_LINK_ERROR = "java/lang/UnsatisfiedLinkError"; + public static final String NAME_IO_EXCEPTION = "java/io/IOException"; + + // Matched constants. + public static final int CLASS_NAME = 0x30000000; + public static final int LOCAL_VARIABLE_INDEX_1 = 0x30000001; + public static final int LOCAL_VARIABLE_INDEX_2 = 0x30000002; + public static final int LOCAL_VARIABLE_INDEX_3 = 0x30000003; + + public static final int CONSTANT_INDEX = InstructionSequenceMatcher.X; + public static final int ACCESS_MODE = InstructionSequenceMatcher.Y; + + public final Instruction[][][] RESOURCE; + public final Constant[] CONSTANTS; + + // Labels. + private final InstructionSequenceReplacer.Label TRY_START = InstructionSequenceReplacer.label(); + private final InstructionSequenceReplacer.Label TRY_END = InstructionSequenceReplacer.label(); + private final InstructionSequenceReplacer.Label CATCH_END = InstructionSequenceReplacer.label(); + + private final InstructionSequenceReplacer.Label CLASS_NOT_FOUND_EXCEPTION; + private final InstructionSequenceReplacer.Label NO_SUCH_METHOD_EXCEPTION; + private final InstructionSequenceReplacer.Label NO_SUCH_FIELD_EXCEPTION; + private final InstructionSequenceReplacer.Label IO_EXCEPTION; + private final InstructionSequenceReplacer.Label RUNTIME_EXCEPTION; + private final InstructionSequenceReplacer.Label UNSATISFIED_LINK_ERROR; + + /** + * Creates a new instance of ResourceIdInstructionSequenceConstants, + * with constants that reference classes from the given class pools. + */ + public ConfigurationLoggingInstructionSequenceConstants(ClassPool programClassPool, + ClassPool libraryClassPool) + { + InstructionSequenceBuilder ____ = + new InstructionSequenceBuilder(programClassPool, libraryClassPool); + + ConstantPoolEditor constantPoolEditor = ____.getConstantPoolEditor(); + + CLASS_NOT_FOUND_EXCEPTION = + catch_(TRY_START.offset(), + TRY_END.offset(), + constantPoolEditor.addClassConstant(NAME_CLASS_NOT_FOUND_EXCEPTION, null)); + NO_SUCH_METHOD_EXCEPTION = + catch_(TRY_START.offset(), + TRY_END.offset(), + constantPoolEditor.addClassConstant(NAME_NO_SUCH_METHOD_EXCEPTION, null)); + NO_SUCH_FIELD_EXCEPTION = + catch_(TRY_START.offset(), + TRY_END.offset(), + constantPoolEditor.addClassConstant(NAME_NO_SUCH_FIELD_EXCEPTION, null)); + IO_EXCEPTION = + catch_(TRY_START.offset(), + TRY_END.offset(), + constantPoolEditor.addClassConstant(NAME_IO_EXCEPTION, null)); + RUNTIME_EXCEPTION = + catch_(TRY_START.offset(), + TRY_END.offset(), + constantPoolEditor.addClassConstant(NAME_RUNTIME_EXCEPTION, null)); + UNSATISFIED_LINK_ERROR = + catch_(TRY_START.offset(), + TRY_END.offset(), + constantPoolEditor.addClassConstant(NAME_UNSATISFIED_LINK_ERROR, null)); + + RESOURCE = new Instruction[][][] + { + // Classes. + { + ____.invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;").__(), + + ____.dup() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(CLASS_NOT_FOUND_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .invokestatic(LOGGER_CLASS_NAME, "logForName", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;").__(), + + ____.dup_x2() + .pop() + .dup_x2() + .pop() + .dup_x2() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(CLASS_NOT_FOUND_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .invokestatic(LOGGER_CLASS_NAME, "logForName", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokevirtual("java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;").__(), + + ____.dup() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokevirtual("java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(CLASS_NOT_FOUND_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .invokestatic(LOGGER_CLASS_NAME, "logLoadClass", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + + // Constructors. + { + ____.invokevirtual("java/lang/Class", "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;").__(), + + ____.dup_x1() + .astore(LOCAL_VARIABLE_INDEX_2) + .dup_x1() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokevirtual("java/lang/Class", "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(NO_SUCH_METHOD_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .aload(LOCAL_VARIABLE_INDEX_2) + .invokestatic(LOGGER_CLASS_NAME, "logGetDeclaredConstructor", "(Ljava/lang/String;Ljava/lang/Class;[Ljava/lang/Class;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokevirtual("java/lang/Class", "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;").__(), + + ____.dup_x1() + .astore(LOCAL_VARIABLE_INDEX_2) + .dup_x1() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokevirtual("java/lang/Class", "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(NO_SUCH_METHOD_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .aload(LOCAL_VARIABLE_INDEX_2) + .invokestatic(LOGGER_CLASS_NAME, "logGetConstructor", "(Ljava/lang/String;Ljava/lang/Class;[Ljava/lang/Class;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokevirtual("java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;").__(), + + ____.dup() + .ldc_(CLASS_NAME) + .swap() + .invokestatic(LOGGER_CLASS_NAME, "logGetDeclaredConstructors", "(Ljava/lang/String;Ljava/lang/Class;)V") + .invokevirtual("java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;").__() + }, + { + ____.invokevirtual("java/lang/Class", "getConstructors", "()[Ljava/lang/reflect/Constructor;").__(), + + ____.dup() + .ldc_(CLASS_NAME) + .swap() + .invokestatic(LOGGER_CLASS_NAME, "logGetConstructors", "(Ljava/lang/String;Ljava/lang/Class;)V") + .invokevirtual("java/lang/Class", "getConstructors", "()[Ljava/lang/reflect/Constructor;").__() + }, + + // Methods. + + { + ____.invokevirtual("java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;").__(), + + ____.dup_x2() + .astore(LOCAL_VARIABLE_INDEX_3) + .dup_x2() + .astore(LOCAL_VARIABLE_INDEX_2) + .dup_x2() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokevirtual("java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(NO_SUCH_METHOD_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .aload(LOCAL_VARIABLE_INDEX_2) + .aload(LOCAL_VARIABLE_INDEX_3) + .invokestatic(LOGGER_CLASS_NAME, "logGetDeclaredMethod", "(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokevirtual("java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;").__(), + + ____.dup_x2() + .astore(LOCAL_VARIABLE_INDEX_3) + .dup_x2() + .astore(LOCAL_VARIABLE_INDEX_2) + .dup_x2() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokevirtual("java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(NO_SUCH_METHOD_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .aload(LOCAL_VARIABLE_INDEX_2) + .aload(LOCAL_VARIABLE_INDEX_3) + .invokestatic(LOGGER_CLASS_NAME, "logGetMethod", "(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokevirtual("java/lang/Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;").__(), + + ____.dup() + .ldc_(CLASS_NAME) + .swap() + .invokestatic(LOGGER_CLASS_NAME, "logGetDeclaredMethods", "(Ljava/lang/String;Ljava/lang/Class;)V") + .invokevirtual("java/lang/Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;").__() + }, + { + ____.invokevirtual("java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;").__(), + + ____.dup() + .ldc_(CLASS_NAME) + .swap() + .invokestatic(LOGGER_CLASS_NAME, "logGetMethods", "(Ljava/lang/String;Ljava/lang/Class;)V") + .invokevirtual("java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;").__() + }, + + // Fields. + + { + ____.invokevirtual("java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;").__(), + + ____.dup_x1() + .astore(LOCAL_VARIABLE_INDEX_2) + .dup_x1() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokevirtual("java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(NO_SUCH_FIELD_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .aload(LOCAL_VARIABLE_INDEX_2) + .invokestatic(LOGGER_CLASS_NAME, "logGetDeclaredField", "(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokevirtual("java/lang/Class", "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;").__(), + + ____.dup_x1() + .astore(LOCAL_VARIABLE_INDEX_2) + .dup_x1() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokevirtual("java/lang/Class", "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;") + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(NO_SUCH_FIELD_EXCEPTION) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .aload(LOCAL_VARIABLE_INDEX_2) + .invokestatic(LOGGER_CLASS_NAME, "logGetField", "(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokevirtual("java/lang/Class", "getDeclaredFields", "()[Ljava/lang/reflect/Field;").__(), + + ____.dup() + .ldc_(CLASS_NAME) + .swap() + .invokestatic(LOGGER_CLASS_NAME, "logGetDeclaredFields", "(Ljava/lang/String;Ljava/lang/Class;)V") + .invokevirtual("java/lang/Class", "getDeclaredFields", "()[Ljava/lang/reflect/Field;").__() + }, + { + ____.invokevirtual("java/lang/Class", "getFields", "()[Ljava/lang/reflect/Field;").__(), + + ____.dup() + .ldc_(CLASS_NAME) + .swap() + .invokestatic(LOGGER_CLASS_NAME, "logGetFields", "(Ljava/lang/String;Ljava/lang/Class;)V") + .invokevirtual("java/lang/Class", "getFields", "()[Ljava/lang/reflect/Field;").__() + }, + + // Resource files. + + // System.loadLibrary(String) + { + ____.ldc_(CONSTANT_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_LANG_SYSTEM, + ClassConstants.METHOD_NAME_LOAD_LIBRARY, + ClassConstants.METHOD_TYPE_LOAD_LIBRARY).__(), + + ____.ldc_(CONSTANT_INDEX) + .label(TRY_START) + .invokestatic(ClassConstants.NAME_JAVA_LANG_SYSTEM, + ClassConstants.METHOD_NAME_LOAD_LIBRARY, + ClassConstants.METHOD_TYPE_LOAD_LIBRARY) + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(UNSATISFIED_LINK_ERROR) + .ldc_(CLASS_NAME) + .ldc_(CONSTANT_INDEX) + .invokestatic(LOGGER_CLASS_NAME, "logLoadLibrary", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokestatic(ClassConstants.NAME_JAVA_LANG_SYSTEM, + ClassConstants.METHOD_NAME_LOAD_LIBRARY, + ClassConstants.METHOD_TYPE_LOAD_LIBRARY).__(), + + ____.dup() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokestatic(ClassConstants.NAME_JAVA_LANG_SYSTEM, + ClassConstants.METHOD_NAME_LOAD_LIBRARY, + ClassConstants.METHOD_TYPE_LOAD_LIBRARY) + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(UNSATISFIED_LINK_ERROR) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .invokestatic(LOGGER_CLASS_NAME, "logLoadLibrary", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + + // System.load(String) + { + ____.ldc_(CONSTANT_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_LANG_SYSTEM, + ClassConstants.METHOD_NAME_LOAD, + ClassConstants.METHOD_TYPE_LOAD).__(), + + ____.ldc_(CONSTANT_INDEX) + .label(TRY_START) + .invokestatic(ClassConstants.NAME_JAVA_LANG_SYSTEM, + ClassConstants.METHOD_NAME_LOAD, + ClassConstants.METHOD_TYPE_LOAD) + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(UNSATISFIED_LINK_ERROR) + .ldc_(CLASS_NAME) + .ldc_(CONSTANT_INDEX) + .invokestatic(LOGGER_CLASS_NAME, "logLoad", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokestatic(ClassConstants.NAME_JAVA_LANG_SYSTEM, + ClassConstants.METHOD_NAME_LOAD, + ClassConstants.METHOD_TYPE_LOAD).__(), + + ____.dup() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokestatic(ClassConstants.NAME_JAVA_LANG_SYSTEM, + ClassConstants.METHOD_NAME_LOAD, + ClassConstants.METHOD_TYPE_LOAD) + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(UNSATISFIED_LINK_ERROR) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .invokestatic(LOGGER_CLASS_NAME, "logLoad", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + + // Runtime.loadLibrary(String) + { + ____.ldc_(CONSTANT_INDEX) + .invokestatic(ClassConstants.NAME_JAVA_LANG_RUNTIME, + ClassConstants.METHOD_NAME_LOAD_LIBRARY, + ClassConstants.METHOD_TYPE_LOAD_LIBRARY).__(), + + ____.ldc_(CONSTANT_INDEX) + .label(TRY_START) + .invokestatic(ClassConstants.NAME_JAVA_LANG_RUNTIME, + ClassConstants.METHOD_NAME_LOAD_LIBRARY, + ClassConstants.METHOD_TYPE_LOAD_LIBRARY) + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(UNSATISFIED_LINK_ERROR) + .ldc_(CLASS_NAME) + .ldc_(CONSTANT_INDEX) + .invokestatic(LOGGER_CLASS_NAME, "logLoadLibrary", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + { + ____.invokestatic(ClassConstants.NAME_JAVA_LANG_RUNTIME, + ClassConstants.METHOD_NAME_LOAD_LIBRARY, + ClassConstants.METHOD_TYPE_LOAD_LIBRARY).__(), + + ____.dup() + .astore(LOCAL_VARIABLE_INDEX_1) + .label(TRY_START) + .invokestatic(ClassConstants.NAME_JAVA_LANG_RUNTIME, + ClassConstants.METHOD_NAME_LOAD_LIBRARY, + ClassConstants.METHOD_TYPE_LOAD_LIBRARY) + .label(TRY_END) + .goto_(CATCH_END.offset()) + .catch_(UNSATISFIED_LINK_ERROR) + .ldc_(CLASS_NAME) + .aload(LOCAL_VARIABLE_INDEX_1) + .invokestatic(LOGGER_CLASS_NAME, "logLoadLibrary", "(Ljava/lang/String;Ljava/lang/String;)V") + .athrow() + .label(CATCH_END).__() + }, + }; + + CONSTANTS = ____.constants(); + } +} diff --git a/core/src/proguard/configuration/ConfigurationLoggingInstructionSequenceReplacer.java b/core/src/proguard/configuration/ConfigurationLoggingInstructionSequenceReplacer.java new file mode 100644 index 000000000..25492966c --- /dev/null +++ b/core/src/proguard/configuration/ConfigurationLoggingInstructionSequenceReplacer.java @@ -0,0 +1,130 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.configuration; + + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.constant.Constant; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.optimize.peephole.*; + +import static proguard.configuration.ConfigurationLoggingInstructionSequenceConstants.LOCAL_VARIABLE_INDEX_1; +import static proguard.configuration.ConfigurationLoggingInstructionSequenceConstants.LOCAL_VARIABLE_INDEX_2; +import static proguard.configuration.ConfigurationLoggingInstructionSequenceConstants.LOCAL_VARIABLE_INDEX_3; + +/** + * This InstructionSequencesReplacer appends logging instructions to all + * instructions calling reflection methods. + * + * @see InstructionSequenceReplacer + * + * @author Johan Leys + */ +public class ConfigurationLoggingInstructionSequenceReplacer extends InstructionSequenceReplacer +{ + public ConfigurationLoggingInstructionSequenceReplacer(InstructionSequenceMatcher instructionSequenceMatcher, + Constant[] patternConstants, + Instruction[] patternInstructions, + Constant[] replacementConstants, + Instruction[] replacementInstructions, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor, + InstructionVisitor extraInstructionVisitor ) + { + super(instructionSequenceMatcher, + patternConstants, + patternInstructions, + replacementConstants, + replacementInstructions, + branchTargetFinder, + codeAttributeEditor, + extraInstructionVisitor ); + } + + + public ConfigurationLoggingInstructionSequenceReplacer(Constant[] patternConstants, + Instruction[] patternInstructions, + Constant[] replacementConstants, + Instruction[] replacementInstructions, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor ) + { + super(patternConstants, + patternInstructions, + replacementConstants, + replacementInstructions, + branchTargetFinder, + codeAttributeEditor ); + } + + + public ConfigurationLoggingInstructionSequenceReplacer(Constant[] patternConstants, + Instruction[] patternInstructions, + Constant[] replacementConstants, + Instruction[] replacementInstructions, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor, + InstructionVisitor extraInstructionVisitor ) + { + super(patternConstants, + patternInstructions, + replacementConstants, + replacementInstructions, + branchTargetFinder, + codeAttributeEditor, + extraInstructionVisitor ); + } + + + @Override + protected int matchedArgument(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int argument) + { + switch (argument) + { + case LOCAL_VARIABLE_INDEX_1: + return codeAttribute.u2maxLocals; + case LOCAL_VARIABLE_INDEX_2: + return codeAttribute.u2maxLocals + 1; + case LOCAL_VARIABLE_INDEX_3: + return codeAttribute.u2maxLocals + 2; + default: + return super.matchedArgument(clazz, argument); + } + } + + + @Override + protected int matchedConstantIndex(ProgramClass programClass, int constantIndex) + { + switch (constantIndex) + { + case ConfigurationLoggingInstructionSequenceConstants.CLASS_NAME: + return new ConstantPoolEditor(programClass) + .addStringConstant(ClassUtil.externalClassName(programClass.getName()), programClass, null); + default: + return super.matchedConstantIndex(programClass, constantIndex); + } + } +} diff --git a/core/src/proguard/configuration/ConfigurationLoggingInstructionSequencesReplacer.java b/core/src/proguard/configuration/ConfigurationLoggingInstructionSequencesReplacer.java new file mode 100644 index 000000000..cb397abbb --- /dev/null +++ b/core/src/proguard/configuration/ConfigurationLoggingInstructionSequencesReplacer.java @@ -0,0 +1,146 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.configuration; + +import proguard.classfile.constant.Constant; +import proguard.classfile.editor.CodeAttributeEditor; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.instruction.visitor.*; +import proguard.optimize.peephole.*; + +/** + * This InstructionSequencesReplacer appends logging instructions to all + * instructions calling reflection methods. + * + * @see InstructionSequencesReplacer + * @see ConfigurationLoggingInstructionSequenceReplacer + * + * @author Johan Leys + */ +public class ConfigurationLoggingInstructionSequencesReplacer +extends MultiInstructionVisitor +implements InstructionVisitor +{ + private static final int PATTERN_INDEX = 0; + private static final int REPLACEMENT_INDEX = 1; + + + /** + * Creates a new ConfigurationLoggingInstructionSequencesReplacer. + * + * @param constants any constants referenced by the pattern + * instructions and replacement instructions. + * @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 ConfigurationLoggingInstructionSequencesReplacer(Constant[] constants, + Instruction[][][] instructionSequences, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor) + { + this(constants, + instructionSequences, + branchTargetFinder, + codeAttributeEditor, + null); + } + + + /** + * Creates a new ConfigurationLoggingInstructionSequencesReplacer. + * + * @param constants any constants referenced by the pattern + * instructions and replacement instructions. + * @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 ConfigurationLoggingInstructionSequencesReplacer(Constant[] constants, + Instruction[][][] instructionSequences, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor, + InstructionVisitor extraInstructionVisitor) + { + super(createInstructionSequenceReplacers(constants, + instructionSequences, + branchTargetFinder, + codeAttributeEditor, + extraInstructionVisitor)); + } + + + /** + * Creates an array of InstructionSequenceReplacer instances. + * + * @param constants any constants referenced by the pattern + * instructions and replacement instructions. + * @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[] constants, + 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 ConfigurationLoggingInstructionSequenceReplacer(constants, + instructionSequencePair[PATTERN_INDEX], + constants, + instructionSequencePair[REPLACEMENT_INDEX], + branchTargetFinder, + codeAttributeEditor, + extraInstructionVisitor); + } + + return instructionSequenceReplacers; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/BasicBranchUnit.java b/core/src/proguard/evaluation/BasicBranchUnit.java similarity index 73% rename from src/proguard/evaluation/BasicBranchUnit.java rename to core/src/proguard/evaluation/BasicBranchUnit.java index ae07632f7..89b3b1ac6 100644 --- a/src/proguard/evaluation/BasicBranchUnit.java +++ b/core/src/proguard/evaluation/BasicBranchUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,37 +26,30 @@ /** * This BranchUnit remembers the branch unit commands that are invoked on it. - * I doesn't consider conditions when branching. + * It doesn't consider conditions when branching. * * @author Eric Lafortune */ public class BasicBranchUnit implements BranchUnit { - private boolean wasCalled; - private InstructionOffsetValue traceBranchTargets; + protected InstructionOffsetValue traceBranchTargets; + protected boolean wasCalled; /** - * Resets the flag that tells whether any of the branch unit commands was - * called. + * Resets the accumulated branch targets and the flag that tells whether + * any of the branch unit methods was called. */ - public void resetCalled() + public void reset() { - wasCalled = false; - } + traceBranchTargets = InstructionOffsetValue.EMPTY_VALUE; - /** - * Sets the flag that tells whether any of the branch unit commands was - * called. - */ - protected void setCalled() - { - wasCalled = true; + wasCalled = false; } /** - * Returns whether any of the branch unit commands was called. + * Returns whether any of the branch unit methods was called. */ public boolean wasCalled() { @@ -65,14 +58,9 @@ public boolean wasCalled() /** - * Sets the initial branch targets, which will be updated as the branch - * methods of the branch unit are called. + * Returns the accumulated branch targets that were passed to the branch + * unit methods. */ - public void setTraceBranchTargets(InstructionOffsetValue branchTargets) - { - this.traceBranchTargets = branchTargets; - } - public InstructionOffsetValue getTraceBranchTargets() { return traceBranchTargets; @@ -101,7 +89,7 @@ public void branchConditionally(Clazz clazz, { // Accumulate the branch targets. traceBranchTargets = - traceBranchTargets.generalize(new InstructionOffsetValue(branchTarget)).instructionOffsetValue(); + traceBranchTargets.add(branchTarget); wasCalled = true; } diff --git a/core/src/proguard/evaluation/BasicInvocationUnit.java b/core/src/proguard/evaluation/BasicInvocationUnit.java new file mode 100644 index 000000000..be38b722d --- /dev/null +++ b/core/src/proguard/evaluation/BasicInvocationUnit.java @@ -0,0 +1,230 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.util.ClassUtil; +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 SimplifiedInvocationUnit +implements InvocationUnit, + MemberVisitor +{ + protected final ValueFactory valueFactory; + + // Field acting as parameter between the visitor methods. + private Clazz returnTypeClass; + + + /** + * Creates a new BasicInvocationUnit with the given value factory. + */ + public BasicInvocationUnit(ValueFactory valueFactory) + { + this.valueFactory = valueFactory; + } + + + // Implementations for SimplifiedInvocationUnit. + + public Value getExceptionValue(Clazz clazz, + ClassConstant catchClassConstant) + { + String catchClassName = catchClassConstant != null ? + catchClassConstant.getName(clazz) : + ClassConstants.NAME_JAVA_LANG_THROWABLE; + + Clazz catchClass = catchClassConstant != null ? + catchClassConstant.referencedClass : + null; + + return valueFactory.createReferenceValue(catchClassName, + catchClass, + true, + false); + } + + + public void setFieldClassValue(Clazz clazz, + RefConstant refConstant, + ReferenceValue value) + { + // We don't care about the new value. + } + + + public 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, + true); + } + + + public void setFieldValue(Clazz clazz, + RefConstant refConstant, + Value value) + { + // We don't care about the new field value. + } + + + public 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, + true); + } + + + public void setMethodParameterValue(Clazz clazz, + RefConstant refConstant, + int parameterIndex, + Value value) + { + // We don't care about the parameter value. + } + + + public Value getMethodParameterValue(Clazz clazz, + Method method, + int parameterIndex, + String type, + Clazz referencedClass) + { + // A "this" parameter can never be null. + boolean isThis = + parameterIndex == 0 && + (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0; + + return valueFactory.createValue(type, + referencedClass, + true, + !isThis); + } + + + public void setMethodReturnValue(Clazz clazz, + Method method, + Value value) + { + // We don't care about the return value. + } + + + public 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, + true); + } + + + /** + * Returns the return value of the specified method. + */ + public Value getMethodReturnValue(Clazz clazz, + InvokeDynamicConstant invokeDynamicConstant, + String type) + { + // Try to figure out the class of the return type. + Clazz[] referencedClasses = invokeDynamicConstant.referencedClasses; + + Clazz referencedClass = + referencedClasses != null && + ClassUtil.isInternalClassType(type) ? + referencedClasses[referencedClasses.length - 1] : + null; + + return valueFactory.createValue(type, + referencedClass, + true, + 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 && + ClassUtil.isInternalClassType(programMethod.getDescriptor(programClass))) + { + returnTypeClass = referencedClasses[referencedClasses.length - 1]; + } + } + + + public void visitLibraryField(LibraryClass programClass, LibraryField libraryField) + { + returnTypeClass = libraryField.referencedClass; + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + Clazz[] referencedClasses = libraryMethod.referencedClasses; + if (referencedClasses != null && + ClassUtil.isInternalClassType(libraryMethod.getDescriptor(libraryClass))) + { + returnTypeClass = referencedClasses[referencedClasses.length - 1]; + } + } +} diff --git a/src/proguard/evaluation/BranchUnit.java b/core/src/proguard/evaluation/BranchUnit.java similarity index 97% rename from src/proguard/evaluation/BranchUnit.java rename to core/src/proguard/evaluation/BranchUnit.java index fb26fc933..4dbead1d9 100644 --- a/src/proguard/evaluation/BranchUnit.java +++ b/core/src/proguard/evaluation/BranchUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/ClassConstantValueFactory.java b/core/src/proguard/evaluation/ClassConstantValueFactory.java similarity index 94% rename from src/proguard/evaluation/ClassConstantValueFactory.java rename to core/src/proguard/evaluation/ClassConstantValueFactory.java index a4e13ce2c..9b4738e65 100644 --- a/src/proguard/evaluation/ClassConstantValueFactory.java +++ b/core/src/proguard/evaluation/ClassConstantValueFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -46,6 +46,7 @@ public void visitClassConstant(Clazz clazz, ClassConstant classConstant) // Create a Class reference instead of a reference to the class. value = valueFactory.createReferenceValue(ClassConstants.NAME_JAVA_LANG_CLASS, classConstant.javaLangClassClass, + false, false); } -} \ No newline at end of file +} diff --git a/src/proguard/evaluation/ConstantValueFactory.java b/core/src/proguard/evaluation/ConstantValueFactory.java similarity index 85% rename from src/proguard/evaluation/ConstantValueFactory.java rename to core/src/proguard/evaluation/ConstantValueFactory.java index 6c6dce0b9..3733e56c1 100644 --- a/src/proguard/evaluation/ConstantValueFactory.java +++ b/core/src/proguard/evaluation/ConstantValueFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -83,10 +83,18 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) value = valueFactory.createDoubleValue(doubleConstant.getValue()); } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + value = valueFactory.createArrayReferenceValue(""+primitiveArrayConstant.getPrimitiveType(), + null, + valueFactory.createIntegerValue(primitiveArrayConstant.getLength())); + } + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { value = valueFactory.createReferenceValue(ClassConstants.NAME_JAVA_LANG_STRING, stringConstant.javaLangStringClass, + false, false); } @@ -94,6 +102,7 @@ public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHa { value = valueFactory.createReferenceValue(ClassConstants.NAME_JAVA_LANG_INVOKE_METHOD_HANDLE, methodHandleConstant.javaLangInvokeMethodHandleClass, + false, false); } @@ -101,13 +110,16 @@ public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { value = valueFactory.createReferenceValue(classConstant.getName(clazz), classConstant.referencedClass, + false, false); } + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) { value = valueFactory.createReferenceValue(ClassConstants.NAME_JAVA_LANG_INVOKE_METHOD_TYPE, methodTypeConstant.javaLangInvokeMethodTypeClass, + false, false); } -} \ No newline at end of file +} diff --git a/src/proguard/evaluation/InvocationUnit.java b/core/src/proguard/evaluation/InvocationUnit.java similarity index 81% rename from src/proguard/evaluation/InvocationUnit.java rename to core/src/proguard/evaluation/InvocationUnit.java index 20cd92164..181414368 100644 --- a/src/proguard/evaluation/InvocationUnit.java +++ b/core/src/proguard/evaluation/InvocationUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -49,6 +49,17 @@ public void exitMethod(Clazz clazz, Value returnValue); + /** + * Sets up the given stack for entering the given exception handler. + */ + public void enterExceptionHandler(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + int catchType, + Stack stack); + + /** * Updates the given stack corresponding to the execution of the given * field or method reference instruction. diff --git a/src/proguard/evaluation/Processor.java b/core/src/proguard/evaluation/Processor.java similarity index 93% rename from src/proguard/evaluation/Processor.java rename to core/src/proguard/evaluation/Processor.java index 30f5322c1..80b7b98c5 100644 --- a/src/proguard/evaluation/Processor.java +++ b/core/src/proguard/evaluation/Processor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -50,6 +50,8 @@ public class Processor * Creates a new processor that operates on the given environment. * @param variables the local variable frame. * @param stack the local stack. + * @param valueFactory the value factory that will create all values + * during the evaluation. * @param branchUnit the class that can affect the program counter. * @param invocationUnit the class that can access other program members. * @param alwaysCast a flag that specifies whether downcasts or casts @@ -67,7 +69,7 @@ public Processor(Variables variables, this.valueFactory = valueFactory; this.branchUnit = branchUnit; this.invocationUnit = invocationUnit; - this.alwaysCast = alwaysCast; + this.alwaysCast = alwaysCast; constantValueFactory = new ConstantValueFactory(valueFactory); classConstantValueFactory = new ClassConstantValueFactory(valueFactory); @@ -597,31 +599,34 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c case InstructionConstants.OP_ANEWARRAY: { - ReferenceValue referenceValue = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); + ReferenceValue arrayType = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); - stack.push(valueFactory.createArrayReferenceValue(referenceValue.internalType(), - referenceValue.getReferencedClass(), + stack.push(valueFactory.createArrayReferenceValue(arrayType.internalType(), + arrayType.getReferencedClass(), stack.ipop())); break; } case InstructionConstants.OP_CHECKCAST: + { // TODO: Check cast. - ReferenceValue castValue = stack.apop(); - ReferenceValue castResultValue = - !alwaysCast && - 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); + ReferenceValue type = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); + + stack.push(stack.apop().cast(type.getType(), + type.getReferencedClass(), + valueFactory, + alwaysCast)); break; + } case InstructionConstants.OP_INSTANCEOF: { - ReferenceValue referenceValue = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); + ReferenceValue value = stack.apop(); + ReferenceValue type = constantValueFactory.constantValue(clazz, constantIndex).referenceValue(); - int instanceOf = stack.apop().instanceOf(referenceValue.getType(), - referenceValue.getReferencedClass()); + int instanceOf = type.mayBeExtension() ? Value.MAYBE : + value.instanceOf(type.getType(), + type.getReferencedClass()); stack.push(instanceOf == Value.NEVER ? valueFactory.createIntegerValue(0) : instanceOf == Value.ALWAYS ? valueFactory.createIntegerValue(1) : @@ -764,6 +769,13 @@ public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute cod { int branchTarget = offset + branchInstruction.branchOffset; + // Maybe branch to the next instruction. + branchUnit.branchConditionally(clazz, + codeAttribute, + offset, + offset + branchInstruction.length(offset), + Value.MAYBE); + switch (branchInstruction.opcode) { case InstructionConstants.OP_IFEQ: @@ -874,26 +886,23 @@ public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribut { 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); + // Maybe branch to the default offset. + branchUnit.branchConditionally(clazz, + codeAttribute, + offset, + offset + tableSwitchInstruction.defaultOffset, + Value.MAYBE); for (int index = 0; index < tableSwitchInstruction.jumpOffsets.length; index++) { int conditional = indexValue.equal(valueFactory.createIntegerValue( tableSwitchInstruction.lowCase + index)); - branchUnit.branchConditionally(clazz, codeAttribute, + + 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; - } } } @@ -902,26 +911,23 @@ public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribu { 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); + // Maybe branch to the default offset. + branchUnit.branchConditionally(clazz, + codeAttribute, + offset, + offset + lookUpSwitchInstruction.defaultOffset, + Value.MAYBE); for (int index = 0; index < lookUpSwitchInstruction.jumpOffsets.length; index++) { int conditional = indexValue.equal(valueFactory.createIntegerValue( lookUpSwitchInstruction.cases[index])); - branchUnit.branchConditionally(clazz, codeAttribute, + + 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/core/src/proguard/evaluation/SimplifiedInvocationUnit.java b/core/src/proguard/evaluation/SimplifiedInvocationUnit.java new file mode 100644 index 000000000..9f7cc86ff --- /dev/null +++ b/core/src/proguard/evaluation/SimplifiedInvocationUnit.java @@ -0,0 +1,314 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; +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 abstract class SimplifiedInvocationUnit +extends SimplifiedVisitor +implements InvocationUnit, + ParameterVisitor, + ConstantVisitor +{ + private final MemberVisitor parameterInitializer = new AllParameterVisitor(true, this); + + // Fields acting as parameters between the visitor methods. + private Variables variables; + protected boolean isStatic; + protected boolean isLoad; + protected Stack stack; + + + // Implementations for InvocationUnit. + + public void enterMethod(Clazz clazz, Method method, Variables variables) + { + // Count the number of parameters, taking into account their categories. + int parameterSize = + ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), + method.getAccessFlags()); + + // Reuse the existing parameters object, ensuring the right size. + variables.reset(parameterSize); + + // Initialize the parameters. + this.variables = variables; + method.accept(clazz, parameterInitializer); + this.variables = null; + } + + + // Implementation for ParameterVisitor. + + public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) + { + Method method = (Method)member; + + // Get the parameter value. + Value value = getMethodParameterValue(clazz, + method, + parameterIndex, + parameterType, + referencedClass); + + // Store the value in the corresponding variable. + variables.store(parameterOffset, value); + } + + + public void exitMethod(Clazz clazz, Method method, Value returnValue) + { + setMethodReturnValue(clazz, method, returnValue); + } + + + public void enterExceptionHandler(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + int catchType, + Stack stack) + { + ClassConstant exceptionClassConstant = + (ClassConstant)((ProgramClass)clazz).getConstant(catchType); + + stack.push(getExceptionValue(clazz, exceptionClassConstant)); + } + + + 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.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.TYPE_VOID) + { + stack.push(getMethodReturnValue(clazz, invokeDynamicConstant, returnType)); + } + } + + + /** + * Returns the value of the specified exception. + */ + public abstract Value getExceptionValue(Clazz clazz, + ClassConstant catchClassConstant); + + + /** + * Sets the class through which the specified field is accessed. + */ + public abstract void setFieldClassValue(Clazz clazz, + RefConstant refConstant, + ReferenceValue value); + + + /** + * Returns the class though which the specified field is accessed. + */ + public abstract Value getFieldClassValue(Clazz clazz, + RefConstant refConstant, + String type); + + + /** + * Sets the value of the specified field. + */ + public abstract void setFieldValue(Clazz clazz, + RefConstant refConstant, + Value value); + + + /** + * Returns the value of the specified field. + */ + public abstract Value getFieldValue(Clazz clazz, + RefConstant refConstant, + String type); + + + /** + * Sets the value of the specified method parameter. + */ + public abstract void setMethodParameterValue(Clazz clazz, + RefConstant refConstant, + int parameterIndex, + Value value); + + + /** + * Returns the value of the specified method parameter. + */ + public abstract Value getMethodParameterValue(Clazz clazz, + Method method, + int parameterIndex, + String type, + Clazz referencedClass); + + + /** + * Sets the return value of the specified method. + */ + public abstract void setMethodReturnValue(Clazz clazz, + Method method, + Value value); + + + /** + * Returns the return value of the specified method. + */ + public abstract Value getMethodReturnValue(Clazz clazz, + RefConstant refConstant, + String type); + + + /** + * Returns the return value of the specified method. + */ + public abstract Value getMethodReturnValue(Clazz clazz, + InvokeDynamicConstant invokeDynamicConstant, + String type); +} diff --git a/src/proguard/evaluation/Stack.java b/core/src/proguard/evaluation/Stack.java similarity index 99% rename from src/proguard/evaluation/Stack.java rename to core/src/proguard/evaluation/Stack.java index 8bac0b5cf..c8fcc0f9a 100644 --- a/src/proguard/evaluation/Stack.java +++ b/core/src/proguard/evaluation/Stack.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/TracedStack.java b/core/src/proguard/evaluation/TracedStack.java similarity index 99% rename from src/proguard/evaluation/TracedStack.java rename to core/src/proguard/evaluation/TracedStack.java index 4d76747f0..6e52ae6c2 100644 --- a/src/proguard/evaluation/TracedStack.java +++ b/core/src/proguard/evaluation/TracedStack.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/TracedVariables.java b/core/src/proguard/evaluation/TracedVariables.java similarity index 99% rename from src/proguard/evaluation/TracedVariables.java rename to core/src/proguard/evaluation/TracedVariables.java index da2deb8e7..4714c3d25 100644 --- a/src/proguard/evaluation/TracedVariables.java +++ b/core/src/proguard/evaluation/TracedVariables.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/Variables.java b/core/src/proguard/evaluation/Variables.java similarity index 99% rename from src/proguard/evaluation/Variables.java rename to core/src/proguard/evaluation/Variables.java index d61364a79..e92fe7d58 100644 --- a/src/proguard/evaluation/Variables.java +++ b/core/src/proguard/evaluation/Variables.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ArrayReferenceValue.java b/core/src/proguard/evaluation/value/ArrayReferenceValue.java similarity index 88% rename from src/proguard/evaluation/value/ArrayReferenceValue.java rename to core/src/proguard/evaluation/value/ArrayReferenceValue.java index 822d006fd..cc021aa0f 100644 --- a/src/proguard/evaluation/value/ArrayReferenceValue.java +++ b/core/src/proguard/evaluation/value/ArrayReferenceValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -39,9 +39,10 @@ class ArrayReferenceValue extends TypedReferenceValue */ public ArrayReferenceValue(String type, Clazz referencedClass, + boolean mayBeExtension, IntegerValue arrayLength) { - super(type, referencedClass, false); + super(type, referencedClass, mayBeExtension, false); this.arrayLength = arrayLength; } @@ -95,6 +96,7 @@ public ReferenceValue generalize(ArrayReferenceValue other) this.type.equals(other.type) && this.referencedClass == other.referencedClass ? new ArrayReferenceValue(this.type, this.referencedClass, + this.mayBeExtension || other.mayBeExtension, this.arrayLength.generalize(other.arrayLength)) : generalize((TypedReferenceValue)other); } @@ -145,9 +147,18 @@ public int equal(ArrayReferenceValue other) public boolean equals(Object object) { - return this == object || - super.equals(object) && - this.arrayLength.equals(((ArrayReferenceValue)object).arrayLength); + if (this == object) + { + return true; + } + + if (!super.equals(object)) + { + return false; + } + + ArrayReferenceValue other = (ArrayReferenceValue)object; + return this.arrayLength.equals(other.arrayLength); } diff --git a/core/src/proguard/evaluation/value/ArrayReferenceValueFactory.java b/core/src/proguard/evaluation/value/ArrayReferenceValueFactory.java new file mode 100644 index 000000000..d564402c1 --- /dev/null +++ b/core/src/proguard/evaluation/value/ArrayReferenceValueFactory.java @@ -0,0 +1,47 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; + +/** + * This identified value factory creates array reference values that also + * represent their elements, in as far as possible. + * + * @author Eric Lafortune + */ +public class ArrayReferenceValueFactory +extends TypedReferenceValueFactory +{ + // Implementations for ReferenceValue. + + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength) + { + return type == null ? + REFERENCE_VALUE_NULL : + new ArrayReferenceValue(ClassConstants.TYPE_ARRAY + type, + referencedClass, + false, + arrayLength); + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ValueFactory.java b/core/src/proguard/evaluation/value/BasicValueFactory.java similarity index 54% rename from src/proguard/evaluation/value/ValueFactory.java rename to core/src/proguard/evaluation/value/BasicValueFactory.java index 888f7326e..64a9bf161 100644 --- a/src/proguard/evaluation/value/ValueFactory.java +++ b/core/src/proguard/evaluation/value/BasicValueFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,25 +28,23 @@ * * @author Eric Lafortune */ -public class ValueFactory +public class BasicValueFactory +implements 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 TypedReferenceValue(null, null, true); - static final ReferenceValue REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL = new TypedReferenceValue(ClassConstants.NAME_JAVA_LANG_OBJECT, null, true); - static final ReferenceValue REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL = new TypedReferenceValue(ClassConstants.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) + 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 = new UnknownReferenceValue(); + + + // Implementations for BasicValueFactory. + + public Value createValue(String type, + Clazz referencedClass, + boolean mayBeExtension, + boolean mayBeNull) { switch (type.charAt(0)) { @@ -60,117 +58,97 @@ public Value createValue(String type, Clazz referencedClass, boolean mayBeNull) case ClassConstants.TYPE_FLOAT: return createFloatValue(); case ClassConstants.TYPE_DOUBLE: return createDoubleValue(); default: return createReferenceValue(ClassUtil.isInternalArrayType(type) ? - type : - ClassUtil.internalClassNameFromClassType(type), + type : + ClassUtil.internalClassNameFromClassType(type), referencedClass, + mayBeExtension, 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 createReferenceValue() + { + return REFERENCE_VALUE; + } + + public ReferenceValue createReferenceValueNull() { - return REFERENCE_VALUE_NULL; + return REFERENCE_VALUE; } - /** - * 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 mayBeExtension, boolean mayBeNull) { - return type == null ? REFERENCE_VALUE_NULL : - !type.equals(ClassConstants.NAME_JAVA_LANG_OBJECT) ? new TypedReferenceValue(type, referencedClass, mayBeNull) : - mayBeNull ? REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : - REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL; + return createReferenceValue(); } - /** - * 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 createReferenceValue(ClassConstants.TYPE_ARRAY + type, - referencedClass, - false); + return createReferenceValue(type, referencedClass, false, false); + } + + + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength, + Value elementValue) + { + return createArrayReferenceValue(type, referencedClass, arrayLength); } } diff --git a/src/proguard/evaluation/value/Category1Value.java b/core/src/proguard/evaluation/value/Category1Value.java similarity index 95% rename from src/proguard/evaluation/value/Category1Value.java rename to core/src/proguard/evaluation/value/Category1Value.java index a9b8a5308..2aafb45b1 100644 --- a/src/proguard/evaluation/value/Category1Value.java +++ b/core/src/proguard/evaluation/value/Category1Value.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/Category2Value.java b/core/src/proguard/evaluation/value/Category2Value.java similarity index 95% rename from src/proguard/evaluation/value/Category2Value.java rename to core/src/proguard/evaluation/value/Category2Value.java index 8229207c1..5a5835bd8 100644 --- a/src/proguard/evaluation/value/Category2Value.java +++ b/core/src/proguard/evaluation/value/Category2Value.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ComparisonValue.java b/core/src/proguard/evaluation/value/ComparisonValue.java similarity index 97% rename from src/proguard/evaluation/value/ComparisonValue.java rename to core/src/proguard/evaluation/value/ComparisonValue.java index d52d2709a..97d46ea7c 100644 --- a/src/proguard/evaluation/value/ComparisonValue.java +++ b/core/src/proguard/evaluation/value/ComparisonValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/CompositeDoubleValue.java b/core/src/proguard/evaluation/value/CompositeDoubleValue.java similarity index 97% rename from src/proguard/evaluation/value/CompositeDoubleValue.java rename to core/src/proguard/evaluation/value/CompositeDoubleValue.java index f060f2d65..5af044cdb 100644 --- a/src/proguard/evaluation/value/CompositeDoubleValue.java +++ b/core/src/proguard/evaluation/value/CompositeDoubleValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/CompositeFloatValue.java b/core/src/proguard/evaluation/value/CompositeFloatValue.java similarity index 97% rename from src/proguard/evaluation/value/CompositeFloatValue.java rename to core/src/proguard/evaluation/value/CompositeFloatValue.java index 88e62617d..e461c6b65 100644 --- a/src/proguard/evaluation/value/CompositeFloatValue.java +++ b/core/src/proguard/evaluation/value/CompositeFloatValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/CompositeIntegerValue.java b/core/src/proguard/evaluation/value/CompositeIntegerValue.java similarity index 98% rename from src/proguard/evaluation/value/CompositeIntegerValue.java rename to core/src/proguard/evaluation/value/CompositeIntegerValue.java index 50d348a2f..cbb87a0f1 100644 --- a/src/proguard/evaluation/value/CompositeIntegerValue.java +++ b/core/src/proguard/evaluation/value/CompositeIntegerValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/CompositeLongValue.java b/core/src/proguard/evaluation/value/CompositeLongValue.java similarity index 96% rename from src/proguard/evaluation/value/CompositeLongValue.java rename to core/src/proguard/evaluation/value/CompositeLongValue.java index 49b509400..0966c38f8 100644 --- a/src/proguard/evaluation/value/CompositeLongValue.java +++ b/core/src/proguard/evaluation/value/CompositeLongValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -43,7 +43,7 @@ final class CompositeLongValue extends SpecificLongValue private final LongValue longValue1; private final byte operation; - private final Value longValue2; + private final Value longValue2; /** diff --git a/src/proguard/evaluation/value/ConvertedByteValue.java b/core/src/proguard/evaluation/value/ConvertedByteValue.java similarity index 96% rename from src/proguard/evaluation/value/ConvertedByteValue.java rename to core/src/proguard/evaluation/value/ConvertedByteValue.java index ffcb24a13..3f297fe02 100644 --- a/src/proguard/evaluation/value/ConvertedByteValue.java +++ b/core/src/proguard/evaluation/value/ConvertedByteValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ConvertedCharacterValue.java b/core/src/proguard/evaluation/value/ConvertedCharacterValue.java similarity index 96% rename from src/proguard/evaluation/value/ConvertedCharacterValue.java rename to core/src/proguard/evaluation/value/ConvertedCharacterValue.java index 3b0706a8d..68f76f6f7 100644 --- a/src/proguard/evaluation/value/ConvertedCharacterValue.java +++ b/core/src/proguard/evaluation/value/ConvertedCharacterValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ConvertedDoubleValue.java b/core/src/proguard/evaluation/value/ConvertedDoubleValue.java similarity index 96% rename from src/proguard/evaluation/value/ConvertedDoubleValue.java rename to core/src/proguard/evaluation/value/ConvertedDoubleValue.java index 0472a2613..adf6db9e8 100644 --- a/src/proguard/evaluation/value/ConvertedDoubleValue.java +++ b/core/src/proguard/evaluation/value/ConvertedDoubleValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ConvertedFloatValue.java b/core/src/proguard/evaluation/value/ConvertedFloatValue.java similarity index 96% rename from src/proguard/evaluation/value/ConvertedFloatValue.java rename to core/src/proguard/evaluation/value/ConvertedFloatValue.java index afa5cf72d..51acd7ba8 100644 --- a/src/proguard/evaluation/value/ConvertedFloatValue.java +++ b/core/src/proguard/evaluation/value/ConvertedFloatValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ConvertedIntegerValue.java b/core/src/proguard/evaluation/value/ConvertedIntegerValue.java similarity index 96% rename from src/proguard/evaluation/value/ConvertedIntegerValue.java rename to core/src/proguard/evaluation/value/ConvertedIntegerValue.java index 2ec9ab860..4c40f37ac 100644 --- a/src/proguard/evaluation/value/ConvertedIntegerValue.java +++ b/core/src/proguard/evaluation/value/ConvertedIntegerValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ConvertedLongValue.java b/core/src/proguard/evaluation/value/ConvertedLongValue.java similarity index 96% rename from src/proguard/evaluation/value/ConvertedLongValue.java rename to core/src/proguard/evaluation/value/ConvertedLongValue.java index 03eb4fd40..168eb0286 100644 --- a/src/proguard/evaluation/value/ConvertedLongValue.java +++ b/core/src/proguard/evaluation/value/ConvertedLongValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ConvertedShortValue.java b/core/src/proguard/evaluation/value/ConvertedShortValue.java similarity index 96% rename from src/proguard/evaluation/value/ConvertedShortValue.java rename to core/src/proguard/evaluation/value/ConvertedShortValue.java index e1f1745eb..f8097bac4 100644 --- a/src/proguard/evaluation/value/ConvertedShortValue.java +++ b/core/src/proguard/evaluation/value/ConvertedShortValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/DetailedArrayReferenceValue.java b/core/src/proguard/evaluation/value/DetailedArrayReferenceValue.java similarity index 84% rename from src/proguard/evaluation/value/DetailedArrayReferenceValue.java rename to core/src/proguard/evaluation/value/DetailedArrayReferenceValue.java index 3f73b91e4..48a7246a4 100644 --- a/src/proguard/evaluation/value/DetailedArrayReferenceValue.java +++ b/core/src/proguard/evaluation/value/DetailedArrayReferenceValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -43,14 +43,16 @@ class DetailedArrayReferenceValue extends IdentifiedArrayReferenceValue */ public DetailedArrayReferenceValue(String type, Clazz referencedClass, + boolean mayBeExtension, IntegerValue arrayLength, ValueFactory valuefactory, int id) { - super(type, referencedClass, arrayLength, valuefactory, id); + super(type, referencedClass, mayBeExtension, arrayLength, valuefactory, id); // Is the array short enough to analyze? if (arrayLength.isParticular() && + arrayLength.value() >= 0 && arrayLength.value() <= MAXIMUM_STORED_ARRAY_LENGTH) { // Initialize the values of the array. @@ -182,6 +184,34 @@ public int equal(ReferenceValue other) } +// // Implementations of binary ReferenceValue methods with +// // UnknownReferenceValue arguments. +// +// public ReferenceValue generalize(UnknownReferenceValue other) +// { +// return other; +// } +// +// +// public int equal(UnknownReferenceValue other) +// { +// return MAYBE; +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TypedReferenceValue arguments. +// +// public ReferenceValue generalize(TypedReferenceValue other) +// { +// } +// +// +// public int equal(TypedReferenceValue other) +// { +// } +// +// // // Implementations of binary ReferenceValue methods with // // IdentifiedReferenceValue arguments. // @@ -239,6 +269,21 @@ public int equal(ReferenceValue other) // public int equal(DetailedArrayReferenceValue other) // { // return equal((IdentifiedArrayReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TracedReferenceValue arguments. +// +// public ReferenceValue generalize(TracedReferenceValue other) +// { +// return other.generalize(this); +// } +// +// +// public int equal(TracedReferenceValue other) +// { +// return other.equal(this); // } @@ -267,9 +312,20 @@ public boolean isParticular() public boolean equals(Object object) { - return this == object || - super.equals(object) && - ArrayUtil.equalOrNull(this.values, ((DetailedArrayReferenceValue)object).values); + if (this == object) + { + return true; + } + + if (!super.equals(object)) + { + return false; + } + + DetailedArrayReferenceValue other = + (DetailedArrayReferenceValue)object; + + return ArrayUtil.equalOrNull(this.values, other.values); } diff --git a/src/proguard/evaluation/value/DetailedValueFactory.java b/core/src/proguard/evaluation/value/DetailedArrayValueFactory.java similarity index 89% rename from src/proguard/evaluation/value/DetailedValueFactory.java rename to core/src/proguard/evaluation/value/DetailedArrayValueFactory.java index 0145523e8..d169f2adb 100644 --- a/src/proguard/evaluation/value/DetailedValueFactory.java +++ b/core/src/proguard/evaluation/value/DetailedArrayValueFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,7 +28,7 @@ * * @author Eric Lafortune */ -public class DetailedValueFactory +public class DetailedArrayValueFactory extends IdentifiedValueFactory { // Implementations for ReferenceValue. @@ -38,9 +38,10 @@ public ReferenceValue createArrayReferenceValue(String type, IntegerValue arrayLength) { return type == null ? - REFERENCE_VALUE_NULL : + TypedReferenceValueFactory.REFERENCE_VALUE_NULL : new DetailedArrayReferenceValue(ClassConstants.TYPE_ARRAY + type, referencedClass, + false, arrayLength, this, referenceID++); diff --git a/src/proguard/evaluation/value/DoubleValue.java b/core/src/proguard/evaluation/value/DoubleValue.java similarity index 99% rename from src/proguard/evaluation/value/DoubleValue.java rename to core/src/proguard/evaluation/value/DoubleValue.java index 44912e7f2..fbac4b0a2 100644 --- a/src/proguard/evaluation/value/DoubleValue.java +++ b/core/src/proguard/evaluation/value/DoubleValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/FloatValue.java b/core/src/proguard/evaluation/value/FloatValue.java similarity index 99% rename from src/proguard/evaluation/value/FloatValue.java rename to core/src/proguard/evaluation/value/FloatValue.java index 515cc862f..bd8457423 100644 --- a/src/proguard/evaluation/value/FloatValue.java +++ b/core/src/proguard/evaluation/value/FloatValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/IdentifiedArrayReferenceValue.java b/core/src/proguard/evaluation/value/IdentifiedArrayReferenceValue.java similarity index 73% rename from src/proguard/evaluation/value/IdentifiedArrayReferenceValue.java rename to core/src/proguard/evaluation/value/IdentifiedArrayReferenceValue.java index 6922dbd0e..da31f07e8 100644 --- a/src/proguard/evaluation/value/IdentifiedArrayReferenceValue.java +++ b/core/src/proguard/evaluation/value/IdentifiedArrayReferenceValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -39,11 +39,12 @@ class IdentifiedArrayReferenceValue extends ArrayReferenceValue */ public IdentifiedArrayReferenceValue(String type, Clazz referencedClass, + boolean mayBeExtension, IntegerValue arrayLength, ValueFactory valuefactory, int id) { - super(type, referencedClass, arrayLength); + super(type, referencedClass, mayBeExtension, arrayLength); this.valuefactory = valuefactory; this.id = id; @@ -64,6 +65,34 @@ public int equal(ReferenceValue other) } +// // Implementations of binary ReferenceValue methods with +// // UnknownReferenceValue arguments. +// +// public ReferenceValue generalize(UnknownReferenceValue other) +// { +// return other; +// } +// +// +// public int equal(UnknownReferenceValue other) +// { +// return MAYBE; +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TypedReferenceValue arguments. +// +// public ReferenceValue generalize(TypedReferenceValue other) +// { +// } +// +// +// public int equal(TypedReferenceValue other) +// { +// } +// +// // // Implementations of binary ReferenceValue methods with // // IdentifiedReferenceValue arguments. // @@ -122,6 +151,21 @@ public int equal(IdentifiedArrayReferenceValue other) // public int equal(DetailedArrayReferenceValue other) // { // return equal((IdentifiedArrayReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TracedReferenceValue arguments. +// +// public ReferenceValue generalize(TracedReferenceValue other) +// { +// return other.generalize(this); +// } +// +// +// public int equal(TracedReferenceValue other) +// { +// return other.equal(this); // } @@ -137,10 +181,21 @@ public boolean isSpecific() public boolean equals(Object object) { - return this == object || - super.equals(object) && - this.valuefactory.equals(((IdentifiedArrayReferenceValue)object).valuefactory) && - this.id == ((IdentifiedArrayReferenceValue)object).id; + if (this == object) + { + return true; + } + + if (!super.equals(object)) + { + return false; + } + + IdentifiedArrayReferenceValue other = + (IdentifiedArrayReferenceValue)object; + + return this.valuefactory.equals(other.valuefactory) && + this.id == other.id; } diff --git a/src/proguard/evaluation/value/IdentifiedDoubleValue.java b/core/src/proguard/evaluation/value/IdentifiedDoubleValue.java similarity index 97% rename from src/proguard/evaluation/value/IdentifiedDoubleValue.java rename to core/src/proguard/evaluation/value/IdentifiedDoubleValue.java index 56cceda12..04be74a39 100644 --- a/src/proguard/evaluation/value/IdentifiedDoubleValue.java +++ b/core/src/proguard/evaluation/value/IdentifiedDoubleValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/IdentifiedFloatValue.java b/core/src/proguard/evaluation/value/IdentifiedFloatValue.java similarity index 97% rename from src/proguard/evaluation/value/IdentifiedFloatValue.java rename to core/src/proguard/evaluation/value/IdentifiedFloatValue.java index cfeb9a98c..aa0a86ca2 100644 --- a/src/proguard/evaluation/value/IdentifiedFloatValue.java +++ b/core/src/proguard/evaluation/value/IdentifiedFloatValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/IdentifiedIntegerValue.java b/core/src/proguard/evaluation/value/IdentifiedIntegerValue.java similarity index 97% rename from src/proguard/evaluation/value/IdentifiedIntegerValue.java rename to core/src/proguard/evaluation/value/IdentifiedIntegerValue.java index e054e8560..f92363dff 100644 --- a/src/proguard/evaluation/value/IdentifiedIntegerValue.java +++ b/core/src/proguard/evaluation/value/IdentifiedIntegerValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/IdentifiedLongValue.java b/core/src/proguard/evaluation/value/IdentifiedLongValue.java similarity index 97% rename from src/proguard/evaluation/value/IdentifiedLongValue.java rename to core/src/proguard/evaluation/value/IdentifiedLongValue.java index d96ffdbe5..ad3775a2c 100644 --- a/src/proguard/evaluation/value/IdentifiedLongValue.java +++ b/core/src/proguard/evaluation/value/IdentifiedLongValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/IdentifiedReferenceValue.java b/core/src/proguard/evaluation/value/IdentifiedReferenceValue.java similarity index 70% rename from src/proguard/evaluation/value/IdentifiedReferenceValue.java rename to core/src/proguard/evaluation/value/IdentifiedReferenceValue.java index 13e63384f..a3a2d7c1a 100644 --- a/src/proguard/evaluation/value/IdentifiedReferenceValue.java +++ b/core/src/proguard/evaluation/value/IdentifiedReferenceValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,12 +23,12 @@ import proguard.classfile.Clazz; /** - * This TypedReferenceValue represents a reference value that is identified by a + * This ReferenceValue represents a reference value that is identified by a * unique ID. * * @author Eric Lafortune */ -class IdentifiedReferenceValue extends TypedReferenceValue +final class IdentifiedReferenceValue extends TypedReferenceValue { private final ValueFactory valuefactory; private final int id; @@ -39,11 +39,12 @@ class IdentifiedReferenceValue extends TypedReferenceValue */ public IdentifiedReferenceValue(String type, Clazz referencedClass, + boolean mayBeExtension, boolean mayBeNull, ValueFactory valuefactory, int id) { - super(type, referencedClass, mayBeNull); + super(type, referencedClass, mayBeExtension, mayBeNull); this.valuefactory = valuefactory; this.id = id; @@ -64,6 +65,34 @@ public int equal(ReferenceValue other) } +// // Implementations of binary ReferenceValue methods with +// // UnknownReferenceValue arguments. +// +// public ReferenceValue generalize(UnknownReferenceValue other) +// { +// return other; +// } +// +// +// public int equal(UnknownReferenceValue other) +// { +// return MAYBE; +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TypedReferenceValue arguments. +// +// public ReferenceValue generalize(TypedReferenceValue other) +// { +// } +// +// +// public int equal(TypedReferenceValue other) +// { +// } + + // Implementations of binary ReferenceValue methods with // IdentifiedReferenceValue arguments. @@ -122,6 +151,21 @@ public int equal(IdentifiedReferenceValue other) // public int equal(DetailedArrayReferenceValue other) // { // return equal((IdentifiedArrayReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TracedReferenceValue arguments. +// +// public ReferenceValue generalize(TracedReferenceValue other) +// { +// return other.generalize(this); +// } +// +// +// public int equal(TracedReferenceValue other) +// { +// return other.equal(this); // } @@ -137,10 +181,19 @@ public boolean isSpecific() public boolean equals(Object object) { - return this == object || - super.equals(object) && - this.valuefactory.equals(((IdentifiedReferenceValue)object).valuefactory) && - this.id == ((IdentifiedReferenceValue)object).id; + if (this == object) + { + return true; + } + + if (!super.equals(object)) + { + return false; + } + + IdentifiedReferenceValue other = (IdentifiedReferenceValue)object; + return this.valuefactory.equals(other.valuefactory) && + this.id == other.id; } @@ -156,4 +209,4 @@ public String toString() { return super.toString()+'#'+id; } -} \ No newline at end of file +} diff --git a/src/proguard/evaluation/value/IdentifiedValueFactory.java b/core/src/proguard/evaluation/value/IdentifiedValueFactory.java similarity index 82% rename from src/proguard/evaluation/value/IdentifiedValueFactory.java rename to core/src/proguard/evaluation/value/IdentifiedValueFactory.java index b999d89a9..5c43cd3b8 100644 --- a/src/proguard/evaluation/value/IdentifiedValueFactory.java +++ b/core/src/proguard/evaluation/value/IdentifiedValueFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,10 +20,12 @@ */ package proguard.evaluation.value; -import proguard.classfile.*; +import proguard.classfile.ClassConstants; +import proguard.classfile.Clazz; /** - * This particular value factory attaches a unique ID to any unknown values. + * This class provides methods to create and reuse Value objects that are + * identified by unique integer IDs. * * @author Eric Lafortune */ @@ -37,7 +39,7 @@ public class IdentifiedValueFactory protected int referenceID; - // Implementations for ValueFactory. + // Implementations for BasicValueFactory. public IntegerValue createIntegerValue() { @@ -65,12 +67,14 @@ public DoubleValue createDoubleValue() public ReferenceValue createReferenceValue(String type, Clazz referencedClass, + boolean mayBeExtension, boolean mayBeNull) { return type == null ? - REFERENCE_VALUE_NULL : + TypedReferenceValueFactory.REFERENCE_VALUE_NULL : new IdentifiedReferenceValue(type, referencedClass, + mayBeExtension, mayBeNull, this, referenceID++); @@ -82,9 +86,10 @@ public ReferenceValue createArrayReferenceValue(String type, IntegerValue arrayLength) { return type == null ? - REFERENCE_VALUE_NULL : + TypedReferenceValueFactory.REFERENCE_VALUE_NULL : new IdentifiedArrayReferenceValue(ClassConstants.TYPE_ARRAY + type, referencedClass, + false, arrayLength, this, referenceID++); diff --git a/src/proguard/evaluation/value/InitialValueFactory.java b/core/src/proguard/evaluation/value/InitialValueFactory.java similarity index 97% rename from src/proguard/evaluation/value/InitialValueFactory.java rename to core/src/proguard/evaluation/value/InitialValueFactory.java index 725b39207..983c86f6c 100644 --- a/src/proguard/evaluation/value/InitialValueFactory.java +++ b/core/src/proguard/evaluation/value/InitialValueFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/InstructionOffsetValue.java b/core/src/proguard/evaluation/value/InstructionOffsetValue.java similarity index 53% rename from src/proguard/evaluation/value/InstructionOffsetValue.java rename to core/src/proguard/evaluation/value/InstructionOffsetValue.java index 69e76bdf1..429dea129 100644 --- a/src/proguard/evaluation/value/InstructionOffsetValue.java +++ b/core/src/proguard/evaluation/value/InstructionOffsetValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,44 +24,63 @@ /** * This class represents a partially evaluated instruction offset. It can - * contain 0 or more specific instruction offsets. + * contain 0 or more specific instruction offsets. Each instruction offset + * can be flagged as an ordinary offset, a method parameter, a method return + * value, a field value, a new instance value, or an exception handler. * * @author Eric Lafortune */ public class InstructionOffsetValue extends Category1Value { - public static final InstructionOffsetValue EMPTY_VALUE = new InstructionOffsetValue(); + private static final int[] EMPTY_OFFSETS = new int[0]; + public static final InstructionOffsetValue EMPTY_VALUE = new InstructionOffsetValue(EMPTY_OFFSETS); - - private int[] values; + public static final int INSTRUCTION_OFFSET_MASK = 0x01ffffff; + public static final int METHOD_PARAMETER = 0x01000000; // Method parameter indices are not really instruction offsets. + public static final int METHOD_RETURN_VALUE = 0x02000000; + public static final int FIELD_VALUE = 0x04000000; + public static final int NEW_INSTANCE = 0x08000000; + public static final int CAST = 0x10000000; + public static final int EXCEPTION_HANDLER = 0x20000000; - private InstructionOffsetValue() - { - } + private int[] values; + /** + * Creates a new InstructionOffsetValue with the given instruction offset. + */ public InstructionOffsetValue(int value) { this.values = new int[] { value }; } + /** + * Creates a new InstructionOffsetValue with the given list of instruction + * offsets. + */ public InstructionOffsetValue(int[] values) { this.values = values; } + /** + * Returns the number of instruction offsets of this value. + */ public int instructionOffsetCount() { - return values == null ? 0 : values.length; + return values.length; } + /** + * Returns the specified instruction offset of this value. + */ public int instructionOffset(int index) { - return values[index]; + return values[index] & INSTRUCTION_OFFSET_MASK; } @@ -71,14 +90,11 @@ public int instructionOffset(int index) */ public boolean contains(int value) { - if (values != null) + for (int index = 0; index < values.length; index++) { - for (int index = 0; index < values.length; index++) + if (values[index] == value) { - if (values[index] == value) - { - return true; - } + return true; } } @@ -94,16 +110,13 @@ public int minimumValue() { int minimumValue = Integer.MAX_VALUE; - if (values != null) + for (int index = 0; index < values.length; index++) { - for (int index = 0; index < values.length; index++) - { - int value = values[index]; + int value = values[index] & INSTRUCTION_OFFSET_MASK; - if (minimumValue > value) - { - minimumValue = value; - } + if (minimumValue > value) + { + minimumValue = value; } } @@ -119,16 +132,13 @@ public int maximumValue() { int maximumValue = Integer.MIN_VALUE; - if (values != null) + for (int index = 0; index < values.length; index++) { - for (int index = 0; index < values.length; index++) - { - int value = values[index]; + int value = values[index] & INSTRUCTION_OFFSET_MASK; - if (maximumValue < value) - { - maximumValue = value; - } + if (maximumValue < value) + { + maximumValue = value; } } @@ -136,22 +146,131 @@ public int maximumValue() } + /** + * Returns whether the specified instruction offset corresponds to a method + * parameter. + */ + public boolean isMethodParameter(int index) + { + return (values[index] & METHOD_PARAMETER) != 0; + } + + + /** + * Returns the specified method parameter (assuming it is one). + */ + public int methodParameter(int index) + { + return values[index] & ~METHOD_PARAMETER; + } + + + /** + * Returns whether the specified instruction offset corresponds to a method + * return value. + */ + public boolean isMethodReturnValue(int index) + { + return (values[index] & METHOD_RETURN_VALUE) != 0; + } + + + /** + * Returns whether the specified instruction offset corresponds to a field + * value. + */ + public boolean isFieldValue(int index) + { + return (values[index] & FIELD_VALUE) != 0; + } + + + /** + * Returns whether the specified instruction offset corresponds to a new + * instance. + */ + public boolean isNewinstance(int index) + { + return (values[index] & NEW_INSTANCE) != 0; + } + + + /** + * Returns whether the specified instruction offset corresponds to a cast. + */ + public boolean isCast(int index) + { + return (values[index] & CAST) != 0; + } + + + /** + * Returns whether the specified instruction offset corresponds to an + * exception handler. + */ + public boolean isExceptionHandler(int index) + { + return (values[index] & EXCEPTION_HANDLER) != 0; + } + + + /** + * Returns an InstructionOffsetValue that contains the instructions offsets + * of this value and the given instruction offset. + */ + public InstructionOffsetValue add(int value) + { + if (contains(value)) + { + return this; + } + + int[] newValues = new int[values.length+1]; + System.arraycopy(values, 0, newValues, 0, values.length); + newValues[values.length] = value; + + return new InstructionOffsetValue(newValues); + } + + + /** + * Returns an InstructionOffsetValue that contains the instructions offsets + * of this value but not the given instruction offset. + */ + public InstructionOffsetValue remove(int value) + { + for (int index = 0; index < values.length; index++) + { + if (values[index] == value) + { + int[] newValues = new int[values.length-1]; + System.arraycopy(values, 0, newValues, 0, index); + System.arraycopy(values, index+1, newValues, index, values.length-index-1); + + return new InstructionOffsetValue(newValues); + } + } + + return this; + } + + /** * 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) + public final InstructionOffsetValue generalize(InstructionOffsetValue other) { - // If the values array of either is null, we can return the other one. + // If the values array of either is empty, we can return the other one. int[] thisValues = this.values; - if (thisValues == null) + if (thisValues.length == 0) { return other; } int[] otherValues = other.values; - if (otherValues == null) + if (otherValues.length == 0) { return this; } @@ -311,7 +430,45 @@ public String toString() { buffer.append(','); } - buffer.append(values[index]); + + if (values[index] < 0) + { + buffer.append(values[index]); + } + else + { + if (isMethodParameter(index)) + { + buffer.append('P'); + } + + if (isMethodReturnValue(index)) + { + buffer.append('M'); + } + + if (isFieldValue(index)) + { + buffer.append('F'); + } + + if (isNewinstance(index)) + { + buffer.append('N'); + } + + if (isCast(index)) + { + buffer.append('C'); + } + + if (isExceptionHandler(index)) + { + buffer.append('E'); + } + + buffer.append(values[index] & 0xffff); + } } } diff --git a/src/proguard/evaluation/value/IntegerValue.java b/core/src/proguard/evaluation/value/IntegerValue.java similarity index 99% rename from src/proguard/evaluation/value/IntegerValue.java rename to core/src/proguard/evaluation/value/IntegerValue.java index ecc68b075..dedd5ab8e 100644 --- a/src/proguard/evaluation/value/IntegerValue.java +++ b/core/src/proguard/evaluation/value/IntegerValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/LongValue.java b/core/src/proguard/evaluation/value/LongValue.java similarity index 99% rename from src/proguard/evaluation/value/LongValue.java rename to core/src/proguard/evaluation/value/LongValue.java index be5795030..fd0f40658 100644 --- a/src/proguard/evaluation/value/LongValue.java +++ b/core/src/proguard/evaluation/value/LongValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/NegatedDoubleValue.java b/core/src/proguard/evaluation/value/NegatedDoubleValue.java similarity index 97% rename from src/proguard/evaluation/value/NegatedDoubleValue.java rename to core/src/proguard/evaluation/value/NegatedDoubleValue.java index f26e8fc7f..6b27dc22b 100644 --- a/src/proguard/evaluation/value/NegatedDoubleValue.java +++ b/core/src/proguard/evaluation/value/NegatedDoubleValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/NegatedFloatValue.java b/core/src/proguard/evaluation/value/NegatedFloatValue.java similarity index 96% rename from src/proguard/evaluation/value/NegatedFloatValue.java rename to core/src/proguard/evaluation/value/NegatedFloatValue.java index 0443a0961..68007e432 100644 --- a/src/proguard/evaluation/value/NegatedFloatValue.java +++ b/core/src/proguard/evaluation/value/NegatedFloatValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/NegatedIntegerValue.java b/core/src/proguard/evaluation/value/NegatedIntegerValue.java similarity index 97% rename from src/proguard/evaluation/value/NegatedIntegerValue.java rename to core/src/proguard/evaluation/value/NegatedIntegerValue.java index 746c90617..a16da1f42 100644 --- a/src/proguard/evaluation/value/NegatedIntegerValue.java +++ b/core/src/proguard/evaluation/value/NegatedIntegerValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/NegatedLongValue.java b/core/src/proguard/evaluation/value/NegatedLongValue.java similarity index 96% rename from src/proguard/evaluation/value/NegatedLongValue.java rename to core/src/proguard/evaluation/value/NegatedLongValue.java index c23cc31c6..0ab6384b8 100644 --- a/src/proguard/evaluation/value/NegatedLongValue.java +++ b/core/src/proguard/evaluation/value/NegatedLongValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ParticularDoubleValue.java b/core/src/proguard/evaluation/value/ParticularDoubleValue.java similarity index 98% rename from src/proguard/evaluation/value/ParticularDoubleValue.java rename to core/src/proguard/evaluation/value/ParticularDoubleValue.java index f61628f24..afe2ea98e 100644 --- a/src/proguard/evaluation/value/ParticularDoubleValue.java +++ b/core/src/proguard/evaluation/value/ParticularDoubleValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -137,7 +137,7 @@ public DoubleValue generalize(ParticularDoubleValue other) // Also handle NaN and Infinity. return Double.doubleToRawLongBits(this.value) == Double.doubleToRawLongBits(other.value) ? - this : ValueFactory.DOUBLE_VALUE; + this : BasicValueFactory.DOUBLE_VALUE; } public DoubleValue add(ParticularDoubleValue other) diff --git a/src/proguard/evaluation/value/ParticularFloatValue.java b/core/src/proguard/evaluation/value/ParticularFloatValue.java similarity index 98% rename from src/proguard/evaluation/value/ParticularFloatValue.java rename to core/src/proguard/evaluation/value/ParticularFloatValue.java index 98875be42..895c560db 100644 --- a/src/proguard/evaluation/value/ParticularFloatValue.java +++ b/core/src/proguard/evaluation/value/ParticularFloatValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -137,7 +137,7 @@ public FloatValue generalize(ParticularFloatValue other) // Also handle NaN and Infinity. return Float.floatToRawIntBits(this.value) == Float.floatToRawIntBits(other.value) ? - this : ValueFactory.FLOAT_VALUE; + this : BasicValueFactory.FLOAT_VALUE; } public FloatValue add(ParticularFloatValue other) diff --git a/src/proguard/evaluation/value/ParticularIntegerValue.java b/core/src/proguard/evaluation/value/ParticularIntegerValue.java similarity index 99% rename from src/proguard/evaluation/value/ParticularIntegerValue.java rename to core/src/proguard/evaluation/value/ParticularIntegerValue.java index 95cf5c57c..16f7e2e05 100644 --- a/src/proguard/evaluation/value/ParticularIntegerValue.java +++ b/core/src/proguard/evaluation/value/ParticularIntegerValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ParticularLongValue.java b/core/src/proguard/evaluation/value/ParticularLongValue.java similarity index 99% rename from src/proguard/evaluation/value/ParticularLongValue.java rename to core/src/proguard/evaluation/value/ParticularLongValue.java index b733dd90a..656c2554d 100644 --- a/src/proguard/evaluation/value/ParticularLongValue.java +++ b/core/src/proguard/evaluation/value/ParticularLongValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/evaluation/value/ParticularValueFactory.java b/core/src/proguard/evaluation/value/ParticularValueFactory.java similarity index 62% rename from src/proguard/evaluation/value/ParticularValueFactory.java rename to core/src/proguard/evaluation/value/ParticularValueFactory.java index 294ab1b3a..72a8fcde8 100644 --- a/src/proguard/evaluation/value/ParticularValueFactory.java +++ b/core/src/proguard/evaluation/value/ParticularValueFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,15 +20,17 @@ */ package proguard.evaluation.value; -import proguard.classfile.*; +import proguard.classfile.Clazz; /** - * This value factory creates particular values. + * This class provides methods to create and reuse Value objects that have + * particular values, whenever they are known. * * @author Eric Lafortune */ public class ParticularValueFactory -extends ValueFactory +extends BasicValueFactory +implements ValueFactory { // Shared copies of Value objects, to avoid creating a lot of objects. static final IntegerValue INTEGER_VALUE_M1 = new ParticularIntegerValue(-1); @@ -51,6 +53,28 @@ public class ParticularValueFactory private static long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0); + private final ValueFactory referenceValueFactory; + + + /** + * Creates a new ParticularValueFactory. + */ + public ParticularValueFactory() + { + this(new ArrayReferenceValueFactory()); + } + + + /** + * Creates a new ParticularValueFactory that delegates to the given + * value factory for creating reference values. + */ + public ParticularValueFactory(ValueFactory referenceValueFactory) + { + this.referenceValueFactory = referenceValueFactory; + } + + // Implementations for ValueFactory. public IntegerValue createIntegerValue(int value) @@ -98,14 +122,48 @@ public DoubleValue createDoubleValue(double value) } + public ReferenceValue createReferenceValue() + { + return referenceValueFactory.createReferenceValue(); + } + + + public ReferenceValue createReferenceValueNull() + { + return referenceValueFactory.createReferenceValueNull(); + } + + + public ReferenceValue createReferenceValue(String type, + Clazz referencedClass, + boolean mayBeExtension, + boolean mayBeNull) + { + return referenceValueFactory.createReferenceValue(type, + referencedClass, + mayBeExtension, + mayBeNull); + } + + public ReferenceValue createArrayReferenceValue(String type, Clazz referencedClass, IntegerValue arrayLength) { - return type == null ? - REFERENCE_VALUE_NULL : - new ArrayReferenceValue(ClassConstants.TYPE_ARRAY + type, - referencedClass, - arrayLength); + return referenceValueFactory.createArrayReferenceValue(type, + referencedClass, + arrayLength); + } + + + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength, + Value elementValue) + { + return referenceValueFactory.createArrayReferenceValue(type, + referencedClass, + arrayLength, + elementValue); } } diff --git a/core/src/proguard/evaluation/value/PrimitiveTypedReferenceValueFactory.java b/core/src/proguard/evaluation/value/PrimitiveTypedReferenceValueFactory.java new file mode 100644 index 000000000..0e18174ed --- /dev/null +++ b/core/src/proguard/evaluation/value/PrimitiveTypedReferenceValueFactory.java @@ -0,0 +1,70 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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. + * Its ReferenceValue objects have types if they represent primitive arrays. + * + * @author Eric Lafortune + */ +public class PrimitiveTypedReferenceValueFactory +extends BasicValueFactory +{ + static final ReferenceValue REFERENCE_VALUE_NULL = new TypedReferenceValue(null, null, false, true); + + // Implementations for BasicValueFactory. + + + public ReferenceValue createReferenceValueNull() + { + return REFERENCE_VALUE_NULL; + } + + + public ReferenceValue createReferenceValue(String type, + Clazz referencedClass, + boolean mayBeExtension, + boolean mayBeNull) + { + return type == null ? REFERENCE_VALUE_NULL : + !ClassUtil.isInternalArrayType(type) || + ClassUtil.isInternalClassType(type) ? REFERENCE_VALUE : + new TypedReferenceValue(type, referencedClass, mayBeExtension, mayBeNull); + } + + + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength) + { + return type == null ? + REFERENCE_VALUE_NULL : + new ArrayReferenceValue(ClassConstants.TYPE_ARRAY + type, + referencedClass, + false, + arrayLength); + } + +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/ReferenceValue.java b/core/src/proguard/evaluation/value/ReferenceValue.java similarity index 77% rename from src/proguard/evaluation/value/ReferenceValue.java rename to core/src/proguard/evaluation/value/ReferenceValue.java index 2f7c4f711..4431dce17 100644 --- a/src/proguard/evaluation/value/ReferenceValue.java +++ b/core/src/proguard/evaluation/value/ReferenceValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -29,11 +29,13 @@ */ public abstract class ReferenceValue extends Category1Value { + // Basic unary methods. + /** * Returns the type. */ public abstract String getType(); -; + /** * Returns the class that is referenced by the type. @@ -41,10 +43,16 @@ public abstract class ReferenceValue extends Category1Value public abstract Clazz getReferencedClass(); - // Basic unary methods. + /** + * Returns whether the actual type of this ReferenceValue may be an + * extension of its type. + */ + public abstract boolean mayBeExtension(); + /** - * Returns whether the type is null. + * Returns whether this ReferenceValue is null. + * @return NEVER, MAYBE, or ALWAYS. */ public abstract int isNull(); @@ -56,10 +64,9 @@ public abstract class ReferenceValue extends Category1Value /** - * Returns a generalization of this ReferenceValue that may be null, - * depending on the flag. + * Returns this ReferenceValue, cast to the given type. */ - public abstract ReferenceValue generalizeMayBeNull(boolean mayBeNull); + public abstract ReferenceValue cast(String type, Clazz referencedClass, ValueFactory valueFactory, boolean alwaysCast); /** @@ -115,7 +122,10 @@ public DoubleValue doubleArrayLoad(IntegerValue indexValue, ValueFactory valueFa * Returns the value of the array at the given index, assuming this type * is a reference array. */ - public abstract ReferenceValue referenceArrayLoad(IntegerValue indexValue, ValueFactory valueFactory); + public ReferenceValue referenceArrayLoad(IntegerValue indexValue, ValueFactory valueFactory) + { + return valueFactory.createReferenceValue(); + } /** @@ -168,6 +178,29 @@ public final int notEqual(ReferenceValue other) } + // Similar binary methods, but this time with unknown arguments. + + /** + * Returns the generalization of this ReferenceValue and the given other + * UnknownReferenceValue. + */ + public ReferenceValue generalize(UnknownReferenceValue other) + { + return other; + } + + + /** + * Returns whether this ReferenceValue is equal to the given other + * UnknownReferenceValue. + * @return NEVER, MAYBE, or ALWAYS. + */ + public int equal(UnknownReferenceValue other) + { + return MAYBE; + } + + // Similar binary methods, but this time with typed reference arguments. /** @@ -286,6 +319,29 @@ public int equal(DetailedArrayReferenceValue other) } + // Similar binary methods, but this time with traced arguments. + + /** + * Returns the generalization of this ReferenceValue and the given other + * TracedReferenceValue. + */ + public ReferenceValue generalize(TracedReferenceValue other) + { + return generalize((ReferenceValue)other); + } + + + /** + * Returns whether this ReferenceValue is equal to the given other + * TracedReferenceValue. + * @return NEVER, MAYBE, or ALWAYS. + */ + public int equal(TracedReferenceValue other) + { + return equal((ReferenceValue)other); + } + + // Implementations for Value. public final ReferenceValue referenceValue() @@ -302,4 +358,25 @@ public final int computationalType() { return TYPE_REFERENCE; } + + + // 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 "a"; + } } diff --git a/src/proguard/evaluation/value/SpecificDoubleValue.java b/core/src/proguard/evaluation/value/SpecificDoubleValue.java similarity index 96% rename from src/proguard/evaluation/value/SpecificDoubleValue.java rename to core/src/proguard/evaluation/value/SpecificDoubleValue.java index 746cd43a0..0eda32ffd 100644 --- a/src/proguard/evaluation/value/SpecificDoubleValue.java +++ b/core/src/proguard/evaluation/value/SpecificDoubleValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -108,7 +108,7 @@ public IntegerValue compare(DoubleValue other) public DoubleValue generalize(SpecificDoubleValue other) { - return this.equals(other) ? this : ValueFactory.DOUBLE_VALUE; + return this.equals(other) ? this : BasicValueFactory.DOUBLE_VALUE; } public DoubleValue add(SpecificDoubleValue other) @@ -153,7 +153,7 @@ public DoubleValue remainderOf(SpecificDoubleValue other) public IntegerValue compare(SpecificDoubleValue other) { - return ValueFactory.INTEGER_VALUE; + return BasicValueFactory.INTEGER_VALUE; // Not handling NaN properly. //return this.equals(other) ? diff --git a/src/proguard/evaluation/value/SpecificFloatValue.java b/core/src/proguard/evaluation/value/SpecificFloatValue.java similarity index 96% rename from src/proguard/evaluation/value/SpecificFloatValue.java rename to core/src/proguard/evaluation/value/SpecificFloatValue.java index 6c6dd9c7e..76a7f99cf 100644 --- a/src/proguard/evaluation/value/SpecificFloatValue.java +++ b/core/src/proguard/evaluation/value/SpecificFloatValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -108,7 +108,7 @@ public IntegerValue compare(FloatValue other) public FloatValue generalize(SpecificFloatValue other) { - return this.equals(other) ? this : ValueFactory.FLOAT_VALUE; + return this.equals(other) ? this : BasicValueFactory.FLOAT_VALUE; } public FloatValue add(SpecificFloatValue other) @@ -153,7 +153,7 @@ public FloatValue remainderOf(SpecificFloatValue other) public IntegerValue compare(SpecificFloatValue other) { - return ValueFactory.INTEGER_VALUE; + return BasicValueFactory.INTEGER_VALUE; // Not handling NaN properly. //return this.equals(other) ? diff --git a/src/proguard/evaluation/value/SpecificIntegerValue.java b/core/src/proguard/evaluation/value/SpecificIntegerValue.java similarity index 98% rename from src/proguard/evaluation/value/SpecificIntegerValue.java rename to core/src/proguard/evaluation/value/SpecificIntegerValue.java index b4befcfc2..c5c33ad77 100644 --- a/src/proguard/evaluation/value/SpecificIntegerValue.java +++ b/core/src/proguard/evaluation/value/SpecificIntegerValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -197,7 +197,7 @@ public int lessThanOrEqual(IntegerValue other) public IntegerValue generalize(SpecificIntegerValue other) { - return this.equals(other) ? this : ValueFactory.INTEGER_VALUE; + return this.equals(other) ? this : BasicValueFactory.INTEGER_VALUE; } public IntegerValue add(SpecificIntegerValue other) diff --git a/src/proguard/evaluation/value/SpecificLongValue.java b/core/src/proguard/evaluation/value/SpecificLongValue.java similarity index 98% rename from src/proguard/evaluation/value/SpecificLongValue.java rename to core/src/proguard/evaluation/value/SpecificLongValue.java index c63ce0324..0e0420a8b 100644 --- a/src/proguard/evaluation/value/SpecificLongValue.java +++ b/core/src/proguard/evaluation/value/SpecificLongValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -142,7 +142,7 @@ public IntegerValue compare(LongValue other) public LongValue generalize(SpecificLongValue other) { - return this.equals(other) ? this : ValueFactory.LONG_VALUE; + return this.equals(other) ? this : BasicValueFactory.LONG_VALUE; } public LongValue add(SpecificLongValue other) diff --git a/src/proguard/evaluation/value/TopValue.java b/core/src/proguard/evaluation/value/TopValue.java similarity index 97% rename from src/proguard/evaluation/value/TopValue.java rename to core/src/proguard/evaluation/value/TopValue.java index eb3a7ea79..35d7989b7 100644 --- a/src/proguard/evaluation/value/TopValue.java +++ b/core/src/proguard/evaluation/value/TopValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/evaluation/value/TracedReferenceValue.java b/core/src/proguard/evaluation/value/TracedReferenceValue.java new file mode 100644 index 000000000..f5f9c24c6 --- /dev/null +++ b/core/src/proguard/evaluation/value/TracedReferenceValue.java @@ -0,0 +1,335 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; +import proguard.optimize.evaluation.ReferenceTracingValueFactory; + +/** + * This ReferenceValue represents a reference value that is tagged with a trace + * value. + * + * @author Eric Lafortune + */ +public class TracedReferenceValue extends ReferenceValue +{ + private final ReferenceValue referenceValue; + private final Value traceValue; + + + /** + * Creates a new reference value with the given ID. + */ + public TracedReferenceValue(ReferenceValue referenceValue, + Value traceValue) + { + this.referenceValue = referenceValue; + this.traceValue = traceValue; + } + + + /** + * Returns the reference value. + */ + public ReferenceValue getReferenceValue() + { + return referenceValue; + } + + + /** + * Returns the trace value. + */ + public Value getTraceValue() + { + return traceValue; + } + + + // Implementations for ReferenceValue. + + public String getType() + { + return referenceValue.getType(); + } + + + public Clazz getReferencedClass() + { + return referenceValue.getReferencedClass(); + } + + + public boolean mayBeExtension() + { + return referenceValue.mayBeExtension(); + } + + + public int isNull() + { + return referenceValue.isNull(); + } + + + public int instanceOf(String otherType, Clazz otherReferencedClass) + { + return referenceValue.instanceOf(otherType, otherReferencedClass); + } + + + public ReferenceValue cast(String type, Clazz referencedClass, ValueFactory valueFactory, boolean alwaysCast) + { + // We're letting the value factory do the cast (either preserving the + // trace value or setting a new one). + return ((ReferenceTracingValueFactory)valueFactory).cast(this, + type, + referencedClass, + alwaysCast); + } + + + public IntegerValue arrayLength(ValueFactory valueFactory) + { + return referenceValue.arrayLength(valueFactory); + } + + + public IntegerValue integerArrayLoad(IntegerValue indexValue, ValueFactory valueFactory) + { + return referenceValue.integerArrayLoad(indexValue, valueFactory); + } + + + public LongValue longArrayLoad(IntegerValue indexValue, ValueFactory valueFactory) + { + return referenceValue.longArrayLoad(indexValue, valueFactory); + } + + + public FloatValue floatArrayLoad(IntegerValue indexValue, ValueFactory valueFactory) + { + return referenceValue.floatArrayLoad(indexValue, valueFactory); + } + + + public DoubleValue doubleArrayLoad(IntegerValue indexValue, ValueFactory valueFactory) + { + return referenceValue.doubleArrayLoad(indexValue, valueFactory); + } + + + public ReferenceValue referenceArrayLoad(IntegerValue indexValue, ValueFactory valueFactory) + { + ReferenceValue value = + referenceValue.referenceArrayLoad(indexValue, valueFactory); + + // We're keeping the existing trace value, if any, or attaching a new + // one otherwise. + return value instanceof TracedReferenceValue ? + value : + ((ReferenceTracingValueFactory)valueFactory).trace(value); + } + + + public void arrayStore(IntegerValue indexValue, Value value) + { + referenceValue.arrayStore(indexValue, value); + } + + + // Implementations of binary methods of ReferenceValue. + + public ReferenceValue generalize(ReferenceValue other) + { + return other.generalize(this); + } + + public int equal(ReferenceValue other) + { + return other.equal(this); + } + + + // Implementations of binary ReferenceValue methods with + // UnknownReferenceValue arguments. + + public ReferenceValue generalize(UnknownReferenceValue other) + { + return new TracedReferenceValue(referenceValue.generalize(other), + traceValue); + } + + public int equal(UnknownReferenceValue other) + { + return referenceValue.equal(other); + } + + + // Implementations of binary ReferenceValue methods with + // TypedReferenceValue arguments. + + public ReferenceValue generalize(TypedReferenceValue other) + { + return new TracedReferenceValue(referenceValue.generalize(other), + traceValue); + } + + public int equal(TypedReferenceValue other) + { + return referenceValue.equal(other); + } + + + // Implementations of binary ReferenceValue methods with + // IdentifiedReferenceValue arguments. + + public ReferenceValue generalize(IdentifiedReferenceValue other) + { + return new TracedReferenceValue(referenceValue.generalize(other), + traceValue); + } + + + public int equal(IdentifiedReferenceValue other) + { + return referenceValue.equal(other); + } + + + // Implementations of binary ReferenceValue methods with + // ArrayReferenceValue arguments. + + public ReferenceValue generalize(ArrayReferenceValue other) + { + return new TracedReferenceValue(referenceValue.generalize(other), + traceValue); + } + + + public int equal(ArrayReferenceValue other) + { + return referenceValue.equal(other); + } + + + // Implementations of binary ReferenceValue methods with + // IdentifiedArrayReferenceValue arguments. + + public ReferenceValue generalize(IdentifiedArrayReferenceValue other) + { + return new TracedReferenceValue(referenceValue.generalize(other), + traceValue); + } + + + public int equal(IdentifiedArrayReferenceValue other) + { + return referenceValue.equal(other); + } + + + // Implementations of binary ReferenceValue methods with + // DetailedArrayReferenceValue arguments. + + public ReferenceValue generalize(DetailedArrayReferenceValue other) + { + return new TracedReferenceValue(referenceValue.generalize(other), + traceValue); + } + + + public int equal(DetailedArrayReferenceValue other) + { + return referenceValue.equal(other); + } + + + // Implementations of binary ReferenceValue methods with + // TracedReferenceValue arguments. + + public ReferenceValue generalize(TracedReferenceValue other) + { + if (this.equals(other)) + { + return this; + } + + return new TracedReferenceValue(this.referenceValue.generalize(other.referenceValue), + this.traceValue .generalize(other.traceValue)); + } + + public int equal(TracedReferenceValue other) + { + return this.referenceValue.equal(other.referenceValue); + } + + + // Implementations for Value. + + public boolean isSpecific() + { + return referenceValue.isSpecific(); + } + + public boolean isParticular() + { + return referenceValue.isParticular(); + } + + public String internalType() + { + return referenceValue.internalType(); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (this == object) + { + return true; + } + + if (!super.equals(object)) + { + return false; + } + + TracedReferenceValue other = (TracedReferenceValue)object; + return this.referenceValue.equals(other.referenceValue) && + this.traceValue .equals(other.traceValue); + } + + + public int hashCode() + { + return referenceValue.hashCode() ^ + traceValue.hashCode(); + } + + + public String toString() + { + return traceValue.toString() + referenceValue.toString(); + } +} \ No newline at end of file diff --git a/core/src/proguard/evaluation/value/TracingValue.java b/core/src/proguard/evaluation/value/TracingValue.java new file mode 100644 index 000000000..bb4bdc06c --- /dev/null +++ b/core/src/proguard/evaluation/value/TracingValue.java @@ -0,0 +1,165 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 value that has been tagged with a sticky trace + * value. + * + * @author Eric Lafortune + */ +public class TracingValue extends Value +{ + private Value traceValue; + private Value value; + + + /** + * Creates a new TracingValue with the given trace value and value. + */ + public TracingValue(Value traceValue, Value value) + { + + this.traceValue = traceValue; + this.value = value; + } + + + /** + * Returns the generalization of this TracingValue and the given other + * TracingValue. + */ + public final TracingValue generalize(TracingValue other) + { + return this.equals(other) ? this : + new TracingValue(this.traceValue.generalize(other.traceValue), + this.value .generalize(other.value)); + } + + + // Implementations for Value. + + public Category1Value category1Value() + { + return value.category1Value(); + } + + public Category2Value category2Value() + { + return value.category2Value(); + } + + public IntegerValue integerValue() + { + return value.integerValue(); + } + + public LongValue longValue() + { + return value.longValue(); + } + + public FloatValue floatValue() + { + return value.floatValue(); + } + + public DoubleValue doubleValue() + { + return value.doubleValue(); + } + + public ReferenceValue referenceValue() + { + return value.referenceValue(); + } + + public final InstructionOffsetValue instructionOffsetValue() + { + return value.instructionOffsetValue(); + } + + public boolean isSpecific() + { + return value.isSpecific(); + } + + public boolean isParticular() + { + return value.isParticular(); + } + + public final Value generalize(Value other) + { + return + other instanceof TracingValue ? generalize((TracingValue)other) : + value.equals(other) ? this : + new TracingValue(traceValue, + value.generalize(other)); + } + + public boolean isCategory2() + { + return value.isCategory2(); + } + + public final int computationalType() + { + return value.computationalType(); + } + + public final String internalType() + { + return value.internalType(); + } + + + // Implementations for Object. + + public boolean equals(Object object) + { + if (object == null || + this.getClass() != object.getClass()) + { + return false; + } + + TracingValue other = (TracingValue)object; + return + this.traceValue.equals(other.traceValue) && + this.value .equals(other.value); + } + + + public int hashCode() + { + return + this.getClass().hashCode() ^ + traceValue.hashCode() ^ + value .hashCode(); + } + + + public String toString() + { + return 'P' + traceValue.toString() + value.toString(); + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/TypedReferenceValue.java b/core/src/proguard/evaluation/value/TypedReferenceValue.java similarity index 60% rename from src/proguard/evaluation/value/TypedReferenceValue.java rename to core/src/proguard/evaluation/value/TypedReferenceValue.java index c4a7ec3fa..754280821 100644 --- a/src/proguard/evaluation/value/TypedReferenceValue.java +++ b/core/src/proguard/evaluation/value/TypedReferenceValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -36,11 +36,14 @@ */ public class TypedReferenceValue extends ReferenceValue { + private static final boolean ALLOW_INCOMPLETE_CLASS_HIERARCHY = System.getProperty("allow.incomplete.class.hierarchy") != null; + private static final boolean DEBUG = false; protected final String type; protected final Clazz referencedClass; + protected final boolean mayBeExtension; protected final boolean mayBeNull; @@ -49,10 +52,12 @@ public class TypedReferenceValue extends ReferenceValue */ public TypedReferenceValue(String type, Clazz referencedClass, + boolean mayBeExtension, boolean mayBeNull) { this.type = type; this.referencedClass = referencedClass; + this.mayBeExtension = mayBeExtension; this.mayBeNull = mayBeNull; } @@ -73,6 +78,11 @@ public Clazz getReferencedClass() // Implementations of unary methods of ReferenceValue. + public boolean mayBeExtension() + { + return mayBeExtension; + } + public int isNull() { return type == null ? ALWAYS : @@ -140,24 +150,15 @@ public int instanceOf(String otherType, Clazz otherReferencedClass) 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.NAME_JAVA_LANG_OBJECT.equals(otherType)) - { - return ALWAYS; - } - - // If this type is an array type, it's ok. - if (thisDimensionCount > otherDimensionCount) + // java.lang.Object, or if this type is an array type, then this type + // is always an instance (unless it may be null). + if (thisType.equals(otherType) || + ClassConstants.NAME_JAVA_LANG_OBJECT.equals(otherType) || + thisDimensionCount > otherDimensionCount) { - return ALWAYS; + return mayBeNull ? MAYBE : + ALWAYS; } // If the other type is an array type, it might be ok. @@ -166,12 +167,34 @@ public int instanceOf(String otherType, Clazz otherReferencedClass) return MAYBE; } - // If the value extends the type, we're sure. - return referencedClass != null && - otherReferencedClass != null && - referencedClass.extendsOrImplements(otherReferencedClass) ? - ALWAYS : - MAYBE; + // If the value extends the type, we're sure (unless it may be null). + // Otherwise, if the value type is final, it can never be an instance. + // Also, if the types are not interfaces and not in the same hierarchy, + // the value can never be an instance. + return referencedClass == null || + otherReferencedClass == null ? MAYBE : + referencedClass.extendsOrImplements(otherReferencedClass) ? mayBeNull ? MAYBE : ALWAYS : + (referencedClass.getAccessFlags() & ClassConstants.ACC_FINAL) != 0 ? NEVER : + (referencedClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 && + (otherReferencedClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 && + !otherReferencedClass.extendsOrImplements(referencedClass) ? NEVER : + MAYBE; + } + + + public ReferenceValue cast(String type, Clazz referencedClass, ValueFactory valueFactory, boolean alwaysCast) + { + // Just return this value if it's the same type. + // Also return this value if it is null or more specific. + return (this.type != null && + this.type.equals(type)) || + (!alwaysCast && + (this.type == null || + instanceOf(this.type, referencedClass) == ALWAYS)) ? this : + valueFactory.createReferenceValue(type, + referencedClass, + true, + mayBeNull); } @@ -179,17 +202,18 @@ public ReferenceValue generalizeMayBeNull(boolean mayBeNull) { return this.mayBeNull == mayBeNull ? this : - new TypedReferenceValue(type, referencedClass, true); + new TypedReferenceValue(type, referencedClass, mayBeExtension, true); } public ReferenceValue referenceArrayLoad(IntegerValue indexValue, ValueFactory valueFactory) { return - type == null ? ValueFactory.REFERENCE_VALUE_NULL : - !ClassUtil.isInternalArrayType(type) ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : + type == null ? TypedReferenceValueFactory.REFERENCE_VALUE_NULL : + !ClassUtil.isInternalArrayType(type) ? TypedReferenceValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : valueFactory.createValue(type.substring(1), referencedClass, + true, true).referenceValue(); } @@ -208,6 +232,21 @@ public int equal(ReferenceValue other) } +// // Implementations of binary ReferenceValue methods with +// // UnknownReferenceValue arguments. +// +// public ReferenceValue generalize(UnknownReferenceValue other) +// { +// return other; +// } +// +// +// public int equal(UnknownReferenceValue other) +// { +// return MAYBE; +// } + + // Implementations of binary ReferenceValue methods with TypedReferenceValue // arguments. @@ -222,10 +261,10 @@ public ReferenceValue generalize(TypedReferenceValue other) String thisType = this.type; String otherType = other.type; - // If both types are nul, the generalization is null too. + // If both types are null, the generalization is null too. if (thisType == null && otherType == null) { - return ValueFactory.REFERENCE_VALUE_NULL; + return TypedReferenceValueFactory.REFERENCE_VALUE_NULL; } // If this type is null, the generalization is the other type, maybe null. @@ -240,12 +279,14 @@ public ReferenceValue generalize(TypedReferenceValue other) return this.generalizeMayBeNull(true); } - boolean mayBeNull = this.mayBeNull || other.mayBeNull; + boolean mayBeExtension = this.mayBeExtension || other.mayBeExtension; + boolean mayBeNull = this.mayBeNull || other.mayBeNull; - // If the two types are equal, the generalization remains the same, maybe null. + // If the two types are equal, the generalization remains the same, + // maybe an extension, maybe null. if (thisType.equals(otherType)) { - return typedReferenceValue(this, mayBeNull); + return typedReferenceValue(this, mayBeExtension, mayBeNull); } // Start taking into account the type dimensions. @@ -259,50 +300,58 @@ public ReferenceValue generalize(TypedReferenceValue other) Clazz thisReferencedClass = this.referencedClass; Clazz otherReferencedClass = other.referencedClass; - // Is one class simply an extension of the other one? - // We're checking the class name instead of the referenced class, - // in case the referenced class is not set, e.g. for a caught - // java.lang.Throwable. - if (thisReferencedClass != null && - thisReferencedClass.extendsOrImplements(ClassUtil.internalClassNameFromClassType(otherType))) - { - return typedReferenceValue(other, mayBeNull); - } - - if (otherReferencedClass != null && - otherReferencedClass.extendsOrImplements(ClassUtil.internalClassNameFromClassType(thisType))) - { - return typedReferenceValue(this, mayBeNull); - } - - // Otherwise, we really need both referenced classes, - // so we can investigate their hierarchies. if (thisReferencedClass != null && otherReferencedClass != null) { - // Do the classes have a non-trivial common superclass? - Clazz commonClass = findCommonClass(thisReferencedClass, - otherReferencedClass, - false); + // Is one class simply an extension of the other one? + if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) + { + return typedReferenceValue(other, true, mayBeNull); + } + + if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) + { + return typedReferenceValue(this, true, mayBeNull); + } - if (commonClass.getName().equals(ClassConstants.NAME_JAVA_LANG_OBJECT)) + try { - // Otherwise, do the classes have a common interface? - Clazz commonInterface = findCommonClass(thisReferencedClass, - otherReferencedClass, - true); - if (commonInterface != null) + // Do the classes have a non-trivial common superclass? + Clazz commonClass = findCommonClass(thisReferencedClass, + otherReferencedClass, + false); + + if (commonClass.getName().equals(ClassConstants.NAME_JAVA_LANG_OBJECT)) { - commonClass = commonInterface; + // Otherwise, do the classes have a common interface? + Clazz commonInterface = findCommonClass(thisReferencedClass, + otherReferencedClass, + true); + if (commonInterface != null) + { + commonClass = commonInterface; + } } + + return new TypedReferenceValue(commonDimensionCount == 0 ? + commonClass.getName() : + ClassUtil.internalArrayTypeFromClassName(commonClass.getName(), + commonDimensionCount), + commonClass, + mayBeExtension, + mayBeNull); } + catch (IllegalArgumentException e) + { + // The class hierarchy seems to be incomplete. + if (ALLOW_INCOMPLETE_CLASS_HIERARCHY) + { + // We'll return an unknown reference value. + return BasicValueFactory.REFERENCE_VALUE; + } - return new TypedReferenceValue(commonDimensionCount == 0 ? - commonClass.getName() : - ClassUtil.internalArrayTypeFromClassName(commonClass.getName(), - commonDimensionCount), - commonClass, - mayBeNull); + throw e; + } } } else if (thisDimensionCount > otherDimensionCount) @@ -310,7 +359,7 @@ else if (thisDimensionCount > otherDimensionCount) // See if the other type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(otherType))) { - return typedReferenceValue(other, mayBeNull); + return typedReferenceValue(other, true, mayBeNull); } } else if (thisDimensionCount < otherDimensionCount) @@ -318,7 +367,7 @@ else if (thisDimensionCount < otherDimensionCount) // See if this type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(thisType))) { - return typedReferenceValue(this, mayBeNull); + return typedReferenceValue(this, true, mayBeNull); } } @@ -336,10 +385,11 @@ else if (thisDimensionCount < otherDimensionCount) commonDimensionCount != 0 ? new TypedReferenceValue(ClassUtil.internalArrayTypeFromClassName(ClassConstants.NAME_JAVA_LANG_OBJECT, commonDimensionCount), null, + true, mayBeNull) : mayBeNull ? - ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : - ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL; + TypedReferenceValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : + TypedReferenceValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL; } @@ -443,6 +493,33 @@ else if (class2.getSuperName() != null) } } + if (commonClass == null) + { + // No common superclass could be found. This usually happens + // when classes are missing in the classpool due to incomplete + // configurations. + + // In case one of the two classes is java/lang/Object itself, + // or is a final class that extends java/lang/Object, we can + // safely assume that java/lang/Object must be the common + // superclass of both. + + for (Clazz clazz : Arrays.asList(class1, class2)) + { + if (ClassConstants.NAME_JAVA_LANG_OBJECT.equals(clazz.getName())) + { + commonClass = clazz; + break; + } + else if ((clazz.getAccessFlags() & ClassConstants.ACC_FINAL) != 0 && + ClassConstants.NAME_JAVA_LANG_OBJECT.equals(clazz.getSuperName())) + { + commonClass = clazz.getSuperClass(); + break; + } + } + } + if (commonClass == null) { throw new IllegalArgumentException("Can't find common super class of ["+ @@ -464,12 +541,15 @@ else if (class2.getSuperName() != null) * that it is a TypedReferenceValue, not a subclass. */ private static ReferenceValue typedReferenceValue(TypedReferenceValue referenceValue, + boolean mayBeExtension, boolean mayBeNull) { - return referenceValue.getClass() == TypedReferenceValue.class ? + return referenceValue.getClass() == TypedReferenceValue.class && + referenceValue.mayBeExtension == mayBeExtension ? referenceValue.generalizeMayBeNull(mayBeNull) : new TypedReferenceValue(referenceValue.type, referenceValue.referencedClass, + mayBeExtension, mayBeNull); } @@ -499,68 +579,94 @@ private int superClassCount(Clazz subClass, Set classes) public int equal(TypedReferenceValue other) { - return this.type == null && other.type == null ? ALWAYS : MAYBE; - } - - - // Implementations of binary ReferenceValue methods with - // IdentifiedReferenceValue arguments. - - public ReferenceValue generalize(IdentifiedReferenceValue other) - { - return generalize((TypedReferenceValue)other); - } - - - public int equal(IdentifiedReferenceValue other) - { - return equal((TypedReferenceValue)other); - } - - - // Implementations of binary ReferenceValue methods with - // ArrayReferenceValue arguments. - - public ReferenceValue generalize(ArrayReferenceValue other) - { - return generalize((TypedReferenceValue)other); - } - - - public int equal(ArrayReferenceValue other) - { - return equal((TypedReferenceValue)other); - } - - - // Implementations of binary ReferenceValue methods with - // IdentifiedArrayReferenceValue arguments. - - public ReferenceValue generalize(IdentifiedArrayReferenceValue other) - { - return generalize((ArrayReferenceValue)other); - } - - - public int equal(IdentifiedArrayReferenceValue other) - { - return equal((ArrayReferenceValue)other); - } - - - // Implementations of binary ReferenceValue methods with - // DetailedArrayReferenceValue arguments. - - public ReferenceValue generalize(DetailedArrayReferenceValue other) - { - return generalize((IdentifiedArrayReferenceValue)other); + return + this.type == null ? + other.type == null ? ALWAYS : + other.mayBeNull ? MAYBE : + NEVER : + other.type == null ? + this.mayBeNull ? MAYBE : + NEVER : + this.mayBeExtension || + other.mayBeExtension || + this.type.equals(other.type) ? MAYBE : + NEVER; } - public int equal(DetailedArrayReferenceValue other) - { - return equal((IdentifiedArrayReferenceValue)other); - } +// // Implementations of binary ReferenceValue methods with +// // IdentifiedReferenceValue arguments. +// +// public ReferenceValue generalize(IdentifiedReferenceValue other) +// { +// return generalize((TypedReferenceValue)other); +// } +// +// +// public int equal(IdentifiedReferenceValue other) +// { +// return equal((TypedReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // ArrayReferenceValue arguments. +// +// public ReferenceValue generalize(ArrayReferenceValue other) +// { +// return generalize((TypedReferenceValue)other); +// } +// +// +// public int equal(ArrayReferenceValue other) +// { +// return equal((TypedReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // IdentifiedArrayReferenceValue arguments. +// +// public ReferenceValue generalize(IdentifiedArrayReferenceValue other) +// { +// return generalize((ArrayReferenceValue)other); +// } +// +// +// public int equal(IdentifiedArrayReferenceValue other) +// { +// return equal((ArrayReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // DetailedArrayReferenceValue arguments. +// +// public ReferenceValue generalize(DetailedArrayReferenceValue other) +// { +// return generalize((IdentifiedArrayReferenceValue)other); +// } +// +// +// public int equal(DetailedArrayReferenceValue other) +// { +// return equal((IdentifiedArrayReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TracedReferenceValue arguments. +// +// public ReferenceValue generalize(TracedReferenceValue other) +// { +// return other.generalize(this); +// } +// +// +// public int equal(TracedReferenceValue other) +// { +// return other.equal(this); +// } // Implementations for Value. @@ -575,7 +681,7 @@ public final String internalType() { return type == null ? ClassConstants.TYPE_JAVA_LANG_OBJECT : - ClassUtil.isInternalArrayType(type) ? type : + ClassUtil.isInternalArrayType(type) ? type : ClassConstants.TYPE_CLASS_START + type + ClassConstants.TYPE_CLASS_END; @@ -591,15 +697,15 @@ public boolean equals(Object object) return true; } - if (object == null || - this.getClass() != object.getClass()) + if (!super.equals(object)) { return false; } TypedReferenceValue other = (TypedReferenceValue)object; return this.type == null ? other.type == null : - (this.mayBeNull == other.mayBeNull && + (this.mayBeExtension == other.mayBeExtension && + this.mayBeNull == other.mayBeNull && this.type.equals(other.type)); } @@ -607,14 +713,18 @@ public boolean equals(Object object) public int hashCode() { return this.getClass().hashCode() ^ - (type == null ? 0 : type.hashCode() ^ (mayBeNull ? 0 : 1)); + (type == null ? 0 : type.hashCode() ^ + (mayBeExtension ? 0 : 1) ^ + (mayBeNull ? 0 : 2)); } public String toString() { - return type == null ? - "null" : - type + (referencedClass == null ? "?" : "") + (mayBeNull ? "" : "!"); + return type == null ? "n" : + type + + (referencedClass == null ? "?" : "") + + (mayBeExtension ? "" : "=") + + (mayBeNull ? "" : "!"); } } diff --git a/core/src/proguard/evaluation/value/TypedReferenceValueFactory.java b/core/src/proguard/evaluation/value/TypedReferenceValueFactory.java new file mode 100644 index 000000000..d6311688a --- /dev/null +++ b/core/src/proguard/evaluation/value/TypedReferenceValueFactory.java @@ -0,0 +1,84 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; + +/** + * This class provides methods to create and reuse Value objects. + * Its ReferenceValue objects have types. + * + * @author Eric Lafortune + */ +public class TypedReferenceValueFactory +extends BasicValueFactory +{ + static final ReferenceValue REFERENCE_VALUE_NULL = new TypedReferenceValue(null, null, false, true); + static final ReferenceValue REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL = new TypedReferenceValue(ClassConstants.NAME_JAVA_LANG_OBJECT, null, true, true); + static final ReferenceValue REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL = new TypedReferenceValue(ClassConstants.NAME_JAVA_LANG_OBJECT, null, true, false); + + + // Implementations for BasicValueFactory. + + public ReferenceValue createReferenceValueNull() + { + return REFERENCE_VALUE_NULL; + } + + + public ReferenceValue createReferenceValue(String type, + Clazz referencedClass, + boolean mayBeExtension, + boolean mayBeNull) + { + return type == null ? REFERENCE_VALUE_NULL : + !type.equals(ClassConstants.NAME_JAVA_LANG_OBJECT) || + !mayBeExtension ? new TypedReferenceValue(type, referencedClass, mayBeExtension, mayBeNull) : + mayBeNull ? REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : + REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL; + } + + + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength) + { + return createArrayReferenceValue(type, + referencedClass, + arrayLength, + createValue(type, + referencedClass, + true, + true)); + } + + + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength, + Value elementValue) + { + return createReferenceValue(ClassConstants.TYPE_ARRAY + type, + referencedClass, + false, + false); + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/UnknownDoubleValue.java b/core/src/proguard/evaluation/value/UnknownDoubleValue.java similarity index 91% rename from src/proguard/evaluation/value/UnknownDoubleValue.java rename to core/src/proguard/evaluation/value/UnknownDoubleValue.java index 7760baca7..34807d1ea 100644 --- a/src/proguard/evaluation/value/UnknownDoubleValue.java +++ b/core/src/proguard/evaluation/value/UnknownDoubleValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -36,17 +36,17 @@ public DoubleValue negate() public IntegerValue convertToInteger() { - return ValueFactory.INTEGER_VALUE; + return BasicValueFactory.INTEGER_VALUE; } public LongValue convertToLong() { - return ValueFactory.LONG_VALUE; + return BasicValueFactory.LONG_VALUE; } public FloatValue convertToFloat() { - return ValueFactory.FLOAT_VALUE; + return BasicValueFactory.FLOAT_VALUE; } @@ -99,7 +99,7 @@ public DoubleValue remainderOf(DoubleValue other) public IntegerValue compare(DoubleValue other) { - return ValueFactory.INTEGER_VALUE; + return BasicValueFactory.INTEGER_VALUE; } diff --git a/src/proguard/evaluation/value/UnknownFloatValue.java b/core/src/proguard/evaluation/value/UnknownFloatValue.java similarity index 91% rename from src/proguard/evaluation/value/UnknownFloatValue.java rename to core/src/proguard/evaluation/value/UnknownFloatValue.java index b343f89f1..0891b756e 100644 --- a/src/proguard/evaluation/value/UnknownFloatValue.java +++ b/core/src/proguard/evaluation/value/UnknownFloatValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -36,17 +36,17 @@ public FloatValue negate() public IntegerValue convertToInteger() { - return ValueFactory.INTEGER_VALUE; + return BasicValueFactory.INTEGER_VALUE; } public LongValue convertToLong() { - return ValueFactory.LONG_VALUE; + return BasicValueFactory.LONG_VALUE; } public DoubleValue convertToDouble() { - return ValueFactory.DOUBLE_VALUE; + return BasicValueFactory.DOUBLE_VALUE; } @@ -99,7 +99,7 @@ public FloatValue remainderOf(FloatValue other) public IntegerValue compare(FloatValue other) { - return ValueFactory.INTEGER_VALUE; + return BasicValueFactory.INTEGER_VALUE; } diff --git a/src/proguard/evaluation/value/UnknownIntegerValue.java b/core/src/proguard/evaluation/value/UnknownIntegerValue.java similarity index 92% rename from src/proguard/evaluation/value/UnknownIntegerValue.java rename to core/src/proguard/evaluation/value/UnknownIntegerValue.java index 51a55a007..9a04834e5 100644 --- a/src/proguard/evaluation/value/UnknownIntegerValue.java +++ b/core/src/proguard/evaluation/value/UnknownIntegerValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -51,17 +51,17 @@ public IntegerValue convertToShort() public LongValue convertToLong() { - return ValueFactory.LONG_VALUE; + return BasicValueFactory.LONG_VALUE; } public FloatValue convertToFloat() { - return ValueFactory.FLOAT_VALUE; + return BasicValueFactory.FLOAT_VALUE; } public DoubleValue convertToDouble() { - return ValueFactory.DOUBLE_VALUE; + return BasicValueFactory.DOUBLE_VALUE; } @@ -150,17 +150,17 @@ public IntegerValue unsignedShiftRightOf(IntegerValue other) public LongValue shiftLeftOf(LongValue other) { - return ValueFactory.LONG_VALUE; + return BasicValueFactory.LONG_VALUE; } public LongValue shiftRightOf(LongValue other) { - return ValueFactory.LONG_VALUE; + return BasicValueFactory.LONG_VALUE; } public LongValue unsignedShiftRightOf(LongValue other) { - return ValueFactory.LONG_VALUE; + return BasicValueFactory.LONG_VALUE; } public IntegerValue and(IntegerValue other) diff --git a/src/proguard/evaluation/value/UnknownLongValue.java b/core/src/proguard/evaluation/value/UnknownLongValue.java similarity index 92% rename from src/proguard/evaluation/value/UnknownLongValue.java rename to core/src/proguard/evaluation/value/UnknownLongValue.java index f431eb688..3715aee55 100644 --- a/src/proguard/evaluation/value/UnknownLongValue.java +++ b/core/src/proguard/evaluation/value/UnknownLongValue.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -36,17 +36,17 @@ public LongValue negate() public IntegerValue convertToInteger() { - return ValueFactory.INTEGER_VALUE; + return BasicValueFactory.INTEGER_VALUE; } public FloatValue convertToFloat() { - return ValueFactory.FLOAT_VALUE; + return BasicValueFactory.FLOAT_VALUE; } public DoubleValue convertToDouble() { - return ValueFactory.DOUBLE_VALUE; + return BasicValueFactory.DOUBLE_VALUE; } @@ -134,7 +134,7 @@ public LongValue xor(LongValue other) public IntegerValue compare(LongValue other) { - return ValueFactory.INTEGER_VALUE; + return BasicValueFactory.INTEGER_VALUE; } diff --git a/core/src/proguard/evaluation/value/UnknownReferenceValue.java b/core/src/proguard/evaluation/value/UnknownReferenceValue.java new file mode 100644 index 000000000..02b4ee615 --- /dev/null +++ b/core/src/proguard/evaluation/value/UnknownReferenceValue.java @@ -0,0 +1,206 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; + +/** + * This class represents a partially evaluated reference value. + * + * @author Eric Lafortune + */ +public class UnknownReferenceValue extends ReferenceValue +{ + // Implementations of unary methods of ReferenceValue. + + public String getType() + { + return ClassConstants.NAME_JAVA_LANG_OBJECT; + } + + public Clazz getReferencedClass() + { + return null; + } + + + public boolean mayBeExtension() + { + return true; + } + + + public int isNull() + { + return MAYBE; + } + + public int instanceOf(String otherType, Clazz otherReferencedClass) + { + return MAYBE; + } + + public ReferenceValue cast(String type, Clazz referencedClass, ValueFactory valueFactory, boolean alwaysCast) + { + return valueFactory.createReferenceValue(type, + referencedClass, + true, + true); + } + + + // Implementations of binary methods of ReferenceValue. + + public ReferenceValue generalize(ReferenceValue other) + { + return other.generalize(this); + } + + public int equal(ReferenceValue other) + { + return other.equal(this); + } + + +// // Implementations of binary ReferenceValue methods with +// // UnknownReferenceValue arguments. +// +// public ReferenceValue generalize(UnknownReferenceValue other) +// { +// return other; +// } +// +// +// public int equal(UnknownReferenceValue other) +// { +// return MAYBE; +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TypedReferenceValue arguments. +// +// public ReferenceValue generalize(TypedReferenceValue other) +// { +// } +// +// +// public int equal(TypedReferenceValue other) +// { +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // IdentifiedReferenceValue arguments. +// +// public ReferenceValue generalize(IdentifiedReferenceValue other) +// { +// return generalize((TypedReferenceValue)other); +// } +// +// +// public int equal(IdentifiedReferenceValue other) +// { +// return equal((TypedReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // ArrayReferenceValue arguments. +// +// public ReferenceValue generalize(ArrayReferenceValue other) +// { +// return generalize((TypedReferenceValue)other); +// } +// +// +// public int equal(ArrayReferenceValue other) +// { +// return equal((TypedReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // IdentifiedArrayReferenceValue arguments. +// +// public ReferenceValue generalize(IdentifiedArrayReferenceValue other) +// { +// return generalize((ArrayReferenceValue)other); +// } +// +// +// public int equal(IdentifiedArrayReferenceValue other) +// { +// return equal((ArrayReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // DetailedArrayReferenceValue arguments. +// +// public ReferenceValue generalize(DetailedArrayReferenceValue other) +// { +// return generalize((IdentifiedArrayReferenceValue)other); +// } +// +// +// public int equal(DetailedArrayReferenceValue other) +// { +// return equal((IdentifiedArrayReferenceValue)other); +// } +// +// +// // Implementations of binary ReferenceValue methods with +// // TracedReferenceValue arguments. +// +// public ReferenceValue generalize(TracedReferenceValue other) +// { +// return other.generalize(this); +// } +// +// +// public int equal(TracedReferenceValue other) +// { +// return other.equal(this); +// } + + + // Implementations for Value. + + public boolean isParticular() + { + return false; + } + + + public final String internalType() + { + return ClassConstants.TYPE_JAVA_LANG_OBJECT; + } + + + // Implementations for Object. + + public String toString() + { + return "a"; + } +} \ No newline at end of file diff --git a/src/proguard/evaluation/value/Value.java b/core/src/proguard/evaluation/value/Value.java similarity index 98% rename from src/proguard/evaluation/value/Value.java rename to core/src/proguard/evaluation/value/Value.java index 58f09a872..30ac6487a 100644 --- a/src/proguard/evaluation/value/Value.java +++ b/core/src/proguard/evaluation/value/Value.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/evaluation/value/ValueFactory.java b/core/src/proguard/evaluation/value/ValueFactory.java new file mode 100644 index 000000000..edadfebc3 --- /dev/null +++ b/core/src/proguard/evaluation/value/ValueFactory.java @@ -0,0 +1,132 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 interface provides methods to create Value objects. + * + * @author Eric Lafortune + */ +public interface ValueFactory +{ + /** + * 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 mayBeExtension, + boolean mayBeNull); + + + /** + * Creates a new IntegerValue with an undefined value. + */ + public IntegerValue createIntegerValue(); + + + /** + * Creates a new IntegerValue with a given particular value. + */ + public IntegerValue createIntegerValue(int value); + + + /** + * Creates a new LongValue with an undefined value. + */ + public LongValue createLongValue(); + + + /** + * Creates a new LongValue with a given particular value. + */ + public LongValue createLongValue(long value); + + + /** + * Creates a new FloatValue with an undefined value. + */ + public FloatValue createFloatValue(); + + + /** + * Creates a new FloatValue with a given particular value. + */ + public FloatValue createFloatValue(float value); + + + /** + * Creates a new DoubleValue with an undefined value. + */ + public DoubleValue createDoubleValue(); + + + /** + * Creates a new DoubleValue with a given particular value. + */ + public DoubleValue createDoubleValue(double value); + + + /** + * Creates a new ReferenceValue of an undefined type. + */ + public ReferenceValue createReferenceValue(); + + + /** + * Creates a new ReferenceValue that represents null. + */ + public ReferenceValue createReferenceValueNull(); + + + /** + * Creates a new ReferenceValue that represents 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 mayBeExtension, + boolean mayBeNull); + + + /** + * Creates a new ReferenceValue that represents a non-null array with + * elements of the given type, with the given length. + */ + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength); + + + /** + * Creates a new ReferenceValue that represents a non-null array with + * elements of the given type, with the given length and initial element + * values. + */ + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength, + Value elementValue); +} diff --git a/src/proguard/evaluation/value/package.html b/core/src/proguard/evaluation/value/package.html similarity index 100% rename from src/proguard/evaluation/value/package.html rename to core/src/proguard/evaluation/value/package.html diff --git a/src/proguard/io/CascadingDataEntryWriter.java b/core/src/proguard/io/CascadingDataEntryWriter.java similarity index 78% rename from src/proguard/io/CascadingDataEntryWriter.java rename to core/src/proguard/io/CascadingDataEntryWriter.java index bb1c85188..bd013398a 100644 --- a/src/proguard/io/CascadingDataEntryWriter.java +++ b/core/src/proguard/io/CascadingDataEntryWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -52,7 +52,6 @@ public CascadingDataEntryWriter(DataEntryWriter dataEntryWriter1, // Implementations for DataEntryWriter. - public boolean createDirectory(DataEntry dataEntry) throws IOException { // Try to create a directory with the first data entry writer, or @@ -62,24 +61,26 @@ public boolean createDirectory(DataEntry dataEntry) throws IOException } - public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) + throws IOException { - return getOutputStream(dataEntry, null); + return dataEntryWriter1.sameOutputStream(dataEntry1, dataEntry2) || + dataEntryWriter2.sameOutputStream(dataEntry1, dataEntry2); } - public OutputStream getOutputStream(DataEntry dataEntry, - Finisher finisher) throws IOException + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException { // Try to get an output stream from the first data entry writer. OutputStream outputStream = - dataEntryWriter1.getOutputStream(dataEntry, finisher); + dataEntryWriter1.createOutputStream(dataEntry); // 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); + dataEntryWriter2.createOutputStream(dataEntry); } @@ -91,4 +92,12 @@ public void close() throws IOException dataEntryWriter1 = null; dataEntryWriter2 = null; } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "CascadingDataEntryWriter"); + dataEntryWriter1.println(pw, prefix + " "); + dataEntryWriter2.println(pw, prefix + " "); + } } diff --git a/core/src/proguard/io/ClassDataEntryWriter.java b/core/src/proguard/io/ClassDataEntryWriter.java new file mode 100644 index 000000000..df76f41d0 --- /dev/null +++ b/core/src/proguard/io/ClassDataEntryWriter.java @@ -0,0 +1,135 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 DataEntryWriter finds received class entries in the given class pool + * and writes them out to the given data entry writer. For resource entries, + * it returns valid output streams. For class entries, it returns output + * streams that must not be used. + * + * @see IdleRewriter + * @author Eric Lafortune + */ +public class ClassDataEntryWriter implements DataEntryWriter +{ + private final ClassPool classPool; + private final DataEntryWriter dataEntryWriter; + + + /** + * Creates a new ClassDataEntryWriter. + * @param classPool the class pool in which classes are found. + * @param dataEntryWriter the writer to which the class file is written. + */ + public ClassDataEntryWriter(ClassPool classPool, + DataEntryWriter dataEntryWriter) + { + this.classPool = classPool; + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryWriter. + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + return dataEntryWriter.createDirectory(dataEntry); + } + + + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) + throws IOException + { + return dataEntryWriter.sameOutputStream(dataEntry1, dataEntry2); + } + + + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException + { + String inputName = dataEntry.getName(); + + // Is it a class entry? + String name = dataEntry.getName(); + if (name.endsWith(ClassConstants.CLASS_FILE_EXTENSION)) + { + // Does it still have a corresponding class? + String className = inputName.substring(0, inputName.length() - ClassConstants.CLASS_FILE_EXTENSION.length()); + Clazz clazz = classPool.getClass(className); + if (clazz != null) + { + // Rename the data entry if necessary. + String newClassName = clazz.getName(); + if (!className.equals(newClassName)) + { + dataEntry = new RenamedDataEntry(dataEntry, newClassName + ClassConstants.CLASS_FILE_EXTENSION); + } + + // Get the output stream for this input entry. + OutputStream outputStream = dataEntryWriter.createOutputStream(dataEntry); + if (outputStream != null) + { + // Write the class to the output stream. + DataOutputStream classOutputStream = new DataOutputStream(outputStream); + try + { + clazz.accept(new ProgramClassWriter(classOutputStream)); + } + catch (RuntimeException e) + { + throw (RuntimeException)new RuntimeException("Unexpected error while writing class ["+className+"] ("+e.getMessage()+")").initCause(e); + } + finally + { + classOutputStream.close(); + } + } + } + + // Return a dummy, non-null output stream (to work with cascading + // output writers). + return new FilterOutputStream(null); + } + + // Delegate for resource entries. + return dataEntryWriter.createOutputStream(dataEntry); + } + + + public void close() throws IOException + { + // Close the delegate writer. + dataEntryWriter.close(); + } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "ClassDataEntryWriter"); + dataEntryWriter.println(pw, prefix + " "); + } +} \ No newline at end of file diff --git a/src/proguard/io/ClassFilter.java b/core/src/proguard/io/ClassFilter.java similarity index 96% rename from src/proguard/io/ClassFilter.java rename to core/src/proguard/io/ClassFilter.java index f5a78a37d..99b78eac3 100644 --- a/src/proguard/io/ClassFilter.java +++ b/core/src/proguard/io/ClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/io/ClassMapDataEntryWriter.java b/core/src/proguard/io/ClassMapDataEntryWriter.java new file mode 100644 index 000000000..77160267d --- /dev/null +++ b/core/src/proguard/io/ClassMapDataEntryWriter.java @@ -0,0 +1,164 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.io; + +import proguard.classfile.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; + +import java.io.*; +import java.util.Iterator; + + +/** + * This DataEntryWriter writes a class mapping to the given data entry, used + * for debugging of the configuration. + * + * Syntax of the mapping file (one line per class): + * + * originalClassName,newClassName,hasObfuscatedMethods,hasObfuscatedFields + * + * hasObfuscatedMethods and hasObfuscatedFields can either take the value + * 0 (false) or 1 (true). + * + * @author Johan Leys + */ +public class ClassMapDataEntryWriter +extends SimplifiedVisitor +implements DataEntryWriter, + + // Implementation interfaces. + MemberVisitor +{ + private final ClassPool programClassPool; + + private final DataEntryWriter dataEntryWriter; + + private boolean obfuscatedMethods = false; + private boolean obfuscatedFields = false; + + + public ClassMapDataEntryWriter(ClassPool programClassPool, + DataEntryWriter dataEntryWriter ) + { + this.programClassPool = programClassPool; + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryWriter. + + public void close() throws IOException + { + dataEntryWriter.close(); + } + + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + return dataEntryWriter.createDirectory(dataEntry); + } + + + public boolean sameOutputStream(DataEntry dataEntry1, DataEntry dataEntry2) throws IOException + { + return dataEntryWriter.sameOutputStream(dataEntry1, dataEntry2); + } + + + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException + { + OutputStream os = dataEntryWriter.createOutputStream(dataEntry); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(os)); + writeClassMap(writer, programClassPool); + writer.close(); + return os; + } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "ClassMapDataEntryWriter"); + dataEntryWriter.println(pw, prefix + " "); + } + + + // Private utility methods. + + private void writeClassMap(PrintWriter writer, ClassPool classPool) + { + Iterator iterator = classPool.classNames(); + while (iterator.hasNext()) + { + String className = (String)iterator.next(); + + StringBuilder builder = new StringBuilder(); + + builder.append(ClassUtil.externalClassName(className)); + builder.append(","); + + ProgramClass clazz = (ProgramClass)classPool.getClass(className); + builder.append(ClassUtil.externalClassName(clazz.getName())); + builder.append(","); + + boolean hasRemovedMethods = (clazz.u2accessFlags & ClassConstants.ACC_REMOVED_METHODS) != 0; + builder.append(hasRemovedMethods || hasObfuscatedMethods(clazz) ? 1 : 0); + builder.append(","); + + boolean hasRemovedFields = (clazz.u2accessFlags & ClassConstants.ACC_REMOVED_FIELDS) != 0; + builder.append(hasRemovedFields || hasObfuscatedFields(clazz) ? 1 : 0); + writer.println(builder.toString()); + } + } + + + private boolean hasObfuscatedMethods(ProgramClass clazz) + { + obfuscatedMethods = false; + clazz.methodsAccept(this); + return obfuscatedMethods; + } + + + private boolean hasObfuscatedFields(ProgramClass clazz) + { + obfuscatedFields = false; + clazz.fieldsAccept(this); + return obfuscatedFields; + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) {} + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + obfuscatedMethods |= (programMethod.getAccessFlags() & ClassConstants.ACC_RENAMED) != 0; + } + + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + obfuscatedFields |= (programField.getAccessFlags() & ClassConstants.ACC_RENAMED) != 0; + } +} diff --git a/core/src/proguard/io/ClassPathDataEntry.java b/core/src/proguard/io/ClassPathDataEntry.java new file mode 100644 index 000000000..df1046fbf --- /dev/null +++ b/core/src/proguard/io/ClassPathDataEntry.java @@ -0,0 +1,118 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; + +import static proguard.classfile.ClassConstants.CLASS_FILE_EXTENSION; +import static proguard.classfile.util.ClassUtil.internalClassName; + +/** + * DataEntry implementation which loads an input stream from the classpath of + * the running VM. + * + * @author Johan Leys + */ +public class ClassPathDataEntry implements DataEntry +{ + private final String name; + + private InputStream inputStream; + + + /** + * Creas an new ClassPathDataEntry for the given class. + * + * @param clazz the class for which to create a data entry. + */ + public ClassPathDataEntry(Class clazz) + { + this(internalClassName(clazz.getName()) + CLASS_FILE_EXTENSION); + } + + + /** + * Creates a new ClassPathDataEntry for the entry with the given name. + * + * @param name the name of the class for which to create a data entry. + */ + public ClassPathDataEntry(String name) + { + this.name = name; + } + + + // Implementations for DataEntry. + + public String getName() + { + return name; + } + + + public String getOriginalName() + { + return name; + } + + + public long getSize() + { + return -1; + } + + + public boolean isDirectory() + { + return false; + } + + + public InputStream getInputStream() throws IOException + { + if (inputStream == null) + { + inputStream = getClass().getClassLoader().getResourceAsStream(name); + } + 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/ClassReader.java b/core/src/proguard/io/ClassReader.java similarity index 92% rename from src/proguard/io/ClassReader.java rename to core/src/proguard/io/ClassReader.java index fbb900ffd..ac288a5a6 100644 --- a/src/proguard/io/ClassReader.java +++ b/core/src/proguard/io/ClassReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -95,7 +95,9 @@ public void read(DataEntry dataEntry) throws IOException String className = clazz.getName(); if (className != null) { - if (!dataEntry.getName().replace(File.pathSeparatorChar, ClassConstants.PACKAGE_SEPARATOR).equals(className+ClassConstants.CLASS_FILE_EXTENSION) && + String dataEntryName = dataEntry.getName(); + if (!dataEntryName.equals("module-info.class") && + !dataEntryName.replace(File.pathSeparatorChar, ClassConstants.PACKAGE_SEPARATOR).equals(className + ClassConstants.CLASS_FILE_EXTENSION) && warningPrinter != null) { warningPrinter.print(className, diff --git a/src/proguard/io/DataEntry.java b/core/src/proguard/io/DataEntry.java similarity index 83% rename from src/proguard/io/DataEntry.java rename to core/src/proguard/io/DataEntry.java index 7ce278ab9..09d1b9886 100644 --- a/src/proguard/io/DataEntry.java +++ b/core/src/proguard/io/DataEntry.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -35,6 +35,20 @@ public interface DataEntry */ public String getName(); + + /** + * Returns the original name of this data entry, i.e. the name of the + * data entry before any renaming or obfuscation. + */ + public String getOriginalName(); + + + /** + * Returns the size of this data entry, in bytes, or -1 if unknown. + */ + public long getSize(); + + /** * Returns whether the data entry represents a directory. */ diff --git a/src/proguard/io/DataEntryClassWriter.java b/core/src/proguard/io/DataEntryClassWriter.java similarity index 87% rename from src/proguard/io/DataEntryClassWriter.java rename to core/src/proguard/io/DataEntryClassWriter.java index 2a078718d..36cf77ab4 100644 --- a/src/proguard/io/DataEntryClassWriter.java +++ b/core/src/proguard/io/DataEntryClassWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -66,15 +66,19 @@ public void visitProgramClass(ProgramClass programClass) try { // Get the output entry corresponding to this input entry. - OutputStream outputStream = dataEntryWriter.getOutputStream(actualDataEntry); + OutputStream outputStream = dataEntryWriter.createOutputStream(actualDataEntry); if (outputStream != null) { // Write the class to the output entry. DataOutputStream classOutputStream = new DataOutputStream(outputStream); - - new ProgramClassWriter(classOutputStream).visitProgramClass(programClass); - - classOutputStream.flush(); + try + { + new ProgramClassWriter(classOutputStream).visitProgramClass(programClass); + } + finally + { + classOutputStream.close(); + } } } catch (IOException e) diff --git a/src/proguard/io/DataEntryCopier.java b/core/src/proguard/io/DataEntryCopier.java similarity index 74% rename from src/proguard/io/DataEntryCopier.java rename to core/src/proguard/io/DataEntryCopier.java index 391596056..7674b3d55 100644 --- a/src/proguard/io/DataEntryCopier.java +++ b/core/src/proguard/io/DataEntryCopier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,11 +20,11 @@ */ package proguard.io; +import proguard.classfile.ClassConstants; import proguard.util.ExtensionMatcher; import java.io.*; - /** * This DataEntryReader writes the ZIP entries and files that it reads to a * given DataEntryWriter. @@ -39,7 +39,9 @@ public class DataEntryCopier implements DataEntryReader private final byte[] buffer = new byte[BUFFER_SIZE]; - + /** + * Creates a new DataEntryCopier. + */ public DataEntryCopier(DataEntryWriter dataEntryWriter) { this.dataEntryWriter = dataEntryWriter; @@ -59,20 +61,31 @@ public void read(DataEntry dataEntry) throws IOException else { // Get the output entry corresponding to this input entry. - OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry); + OutputStream outputStream = dataEntryWriter.createOutputStream(dataEntry); if (outputStream != null) { - InputStream inputStream = dataEntry.getInputStream(); - try { - // Copy the data from the input entry to the output entry. - copyData(inputStream, outputStream); + InputStream inputStream = dataEntry.getInputStream(); + + try + { + // Copy the data from the input entry to the output entry. + copyData(inputStream, outputStream); + + // Flush the output stream, just to be sure. + outputStream.flush(); + } + finally + { + // Close the input stream. + dataEntry.closeInputStream(); + } } finally { - // Close the data entries. - dataEntry.closeInputStream(); + // Close the output stream. + outputStream.close(); } } } @@ -90,7 +103,10 @@ public void read(DataEntry dataEntry) throws IOException /** * Copies all data that it can read from the given input stream to the - * given output stream. + * given output stream. The caller of this method will open and + * afterwards flush and close the input stream and the output stream. + * The implementation of this method needs to make sure that any wrapping + * output streams are flushed before returning. */ protected void copyData(InputStream inputStream, OutputStream outputStream) @@ -105,13 +121,11 @@ protected void copyData(InputStream inputStream, } outputStream.write(buffer, 0, count); } - - outputStream.flush(); } /** - * A main method for testing file/jar/war/directory copying. + * A main method for testing file/archive/directory copying. */ public static void main(String[] args) { @@ -120,20 +134,22 @@ public static void main(String[] args) String input = args[0]; String output = args[1]; - boolean outputIsApk = output.endsWith(".apk") || - output.endsWith(".ap_"); - boolean outputIsJar = output.endsWith(".jar"); - boolean outputIsAar = output.endsWith(".aar"); - boolean outputIsWar = output.endsWith(".war"); - boolean outputIsEar = output.endsWith(".ear"); - boolean outputIsZip = output.endsWith(".zip"); + boolean outputIsApk = output.endsWith(".apk") || + output.endsWith(".ap_"); + boolean outputIsJar = output.endsWith(".jar"); + boolean outputIsAar = output.endsWith(".aar"); + boolean outputIsWar = output.endsWith(".war"); + boolean outputIsEar = output.endsWith(".ear"); + boolean outputIsJmod = output.endsWith(".jmod"); + boolean outputIsZip = output.endsWith(".zip"); DataEntryWriter writer = new DirectoryWriter(new File(output), - outputIsApk || - outputIsJar || - outputIsAar || - outputIsWar || - outputIsEar || + outputIsApk || + outputIsJar || + outputIsAar || + outputIsWar || + outputIsEar || + outputIsJmod || outputIsZip); // Zip up any zips, if necessary. @@ -153,20 +169,20 @@ public static void main(String[] args) writer); } - // Zip up any ears, if necessary. - DataEntryWriter earWriter = new JarWriter(writer); - if (outputIsEar) + // Zip up any jmods, if necessary. + DataEntryWriter jmodWriter = new JarWriter(ClassConstants.JMOD_HEADER, writer); + if (outputIsJmod) { // Always zip. - writer = earWriter; + writer = jmodWriter; } else { - // Only zip up ears. + // Only zip up jmods. writer = new FilteredDataEntryWriter(new DataEntryParentFilter( new DataEntryNameFilter( - new ExtensionMatcher(".ear"))), - earWriter, + new ExtensionMatcher(".jmod"))), + jmodWriter, writer); } @@ -189,7 +205,7 @@ public static void main(String[] args) // Zip up any aars, if necessary. DataEntryWriter aarWriter = new JarWriter(writer); - if (outputIsAar) + if (outputIsWar) { // Always zip. writer = aarWriter; @@ -242,13 +258,14 @@ public static void main(String[] args) // Create the copying DataEntryReader. DataEntryReader reader = new DataEntryCopier(writer); - boolean inputIsApk = input.endsWith(".apk") || - input.endsWith(".ap_"); - boolean inputIsJar = input.endsWith(".jar"); - boolean inputIsAar = input.endsWith(".aar"); - boolean inputIsWar = input.endsWith(".war"); - boolean inputIsEar = input.endsWith(".ear"); - boolean inputIsZip = input.endsWith(".zip"); + boolean inputIsApk = input.endsWith(".apk") || + input.endsWith(".ap_"); + boolean inputIsJar = input.endsWith(".jar"); + boolean inputIsAar = input.endsWith(".aar"); + boolean inputIsWar = input.endsWith(".war"); + boolean inputIsEar = input.endsWith(".ear"); + boolean inputIsJmod = input.endsWith(".jmod"); + boolean inputIsZip = input.endsWith(".zip"); // Unzip any apks, if necessary. DataEntryReader apkReader = new JarReader(reader); @@ -325,20 +342,36 @@ public static void main(String[] args) earReader, reader); - // Unzip any zips, if necessary. - DataEntryReader zipReader = new JarReader(reader); - if (inputIsZip) + // Unzip any jmods, if necessary. + DataEntryReader jmodReader = new JarReader(reader, true); + if (inputIsJmod) { // Always unzip. - reader = zipReader; + reader = jmodReader; } else { - // Only unzip zip entries. + // Only unzip jmod entries. reader = new FilteredDataEntryReader(new DataEntryNameFilter( - new ExtensionMatcher(".zip")), - zipReader, + new ExtensionMatcher(".jmod")), + jmodReader, 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); + } } } } diff --git a/src/proguard/io/DataEntryDirectoryFilter.java b/core/src/proguard/io/DataEntryDirectoryFilter.java similarity index 95% rename from src/proguard/io/DataEntryDirectoryFilter.java rename to core/src/proguard/io/DataEntryDirectoryFilter.java index beefc3b72..62f9904c1 100644 --- a/src/proguard/io/DataEntryDirectoryFilter.java +++ b/core/src/proguard/io/DataEntryDirectoryFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/io/DataEntryFilter.java b/core/src/proguard/io/DataEntryFilter.java similarity index 95% rename from src/proguard/io/DataEntryFilter.java rename to core/src/proguard/io/DataEntryFilter.java index fe9f64778..1bc5d7f2c 100644 --- a/src/proguard/io/DataEntryFilter.java +++ b/core/src/proguard/io/DataEntryFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/io/DataEntryNameFilter.java b/core/src/proguard/io/DataEntryNameFilter.java similarity index 96% rename from src/proguard/io/DataEntryNameFilter.java rename to core/src/proguard/io/DataEntryNameFilter.java index e7f65c00f..9dd97f0b1 100644 --- a/src/proguard/io/DataEntryNameFilter.java +++ b/core/src/proguard/io/DataEntryNameFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/io/DataEntryParentFilter.java b/core/src/proguard/io/DataEntryParentFilter.java similarity index 96% rename from src/proguard/io/DataEntryParentFilter.java rename to core/src/proguard/io/DataEntryParentFilter.java index b01bc718d..e2be6f94a 100644 --- a/src/proguard/io/DataEntryParentFilter.java +++ b/core/src/proguard/io/DataEntryParentFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/io/DataEntryPump.java b/core/src/proguard/io/DataEntryPump.java similarity index 96% rename from src/proguard/io/DataEntryPump.java rename to core/src/proguard/io/DataEntryPump.java index b6cb53283..e6448574c 100644 --- a/src/proguard/io/DataEntryPump.java +++ b/core/src/proguard/io/DataEntryPump.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/io/DataEntryReader.java b/core/src/proguard/io/DataEntryReader.java similarity index 95% rename from src/proguard/io/DataEntryReader.java rename to core/src/proguard/io/DataEntryReader.java index e41b6eb2b..5dd1701a1 100644 --- a/src/proguard/io/DataEntryReader.java +++ b/core/src/proguard/io/DataEntryReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/io/DataEntryRewriter.java b/core/src/proguard/io/DataEntryRewriter.java similarity index 93% rename from src/proguard/io/DataEntryRewriter.java rename to core/src/proguard/io/DataEntryRewriter.java index 827a93269..f9117dd35 100644 --- a/src/proguard/io/DataEntryRewriter.java +++ b/core/src/proguard/io/DataEntryRewriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,7 @@ import proguard.classfile.*; import java.io.*; +import java.nio.charset.Charset; /** * This DataEntryReader writes the resource data entries that it reads to a @@ -34,17 +35,20 @@ public class DataEntryRewriter extends DataEntryCopier { private final ClassPool classPool; + private final Charset charset; /** * Creates a new DataEntryRewriter. */ public DataEntryRewriter(ClassPool classPool, + Charset charset, DataEntryWriter dataEntryWriter) { super(dataEntryWriter); this.classPool = classPool; + this.charset = charset; } @@ -54,13 +58,12 @@ protected void copyData(InputStream inputStream, OutputStream outputStream) throws IOException { - Reader reader = new BufferedReader(new InputStreamReader(inputStream)); - Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream)); + Reader reader = new BufferedReader(new InputStreamReader(inputStream, charset)); + Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream, charset)); copyData(reader, writer); writer.flush(); - outputStream.flush(); } @@ -127,7 +130,7 @@ private void writeUpdatedWord(Writer writer, String word) word.replace('.', ClassConstants.PACKAGE_SEPARATOR) : word; - // Find the class corrsponding to the word. + // Find the class corresponding to the word. Clazz clazz = classPool.getClass(className); if (clazz != null) { diff --git a/src/proguard/io/DataEntryWriter.java b/core/src/proguard/io/DataEntryWriter.java similarity index 57% rename from src/proguard/io/DataEntryWriter.java rename to core/src/proguard/io/DataEntryWriter.java index 1fe2ea6a6..819a5d417 100644 --- a/src/proguard/io/DataEntryWriter.java +++ b/core/src/proguard/io/DataEntryWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,7 +22,6 @@ 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 @@ -41,33 +40,36 @@ public interface DataEntryWriter /** - * 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. + * Returns whether the two given data entries would result in the same + * output stream. + * @param dataEntry1 the first data entry. + * @param dataEntry2 the second data entry. */ - public OutputStream getOutputStream(DataEntry dataEntry) throws IOException; + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) 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. + * Creates a new output stream for writing data. The caller is responsible + * for closing the stream. + * @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, - Finisher finisher) throws IOException; + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException; /** * Finishes writing all data entries. */ public void close() throws IOException; + + + /** + * Prints out the structure of the data entry writer. + * @param pw the print stream to which the structure should be printed. + * @param prefix a prefix for every printed line. + */ + public void println(PrintWriter pw, String prefix); } diff --git a/src/proguard/io/DirectoryFilter.java b/core/src/proguard/io/DirectoryFilter.java similarity index 96% rename from src/proguard/io/DirectoryFilter.java rename to core/src/proguard/io/DirectoryFilter.java index a973b5cc4..1cc5aea0a 100644 --- a/src/proguard/io/DirectoryFilter.java +++ b/core/src/proguard/io/DirectoryFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/io/DirectoryPump.java b/core/src/proguard/io/DirectoryPump.java similarity index 90% rename from src/proguard/io/DirectoryPump.java rename to core/src/proguard/io/DirectoryPump.java index f191fabe9..cfab9746d 100644 --- a/src/proguard/io/DirectoryPump.java +++ b/core/src/proguard/io/DirectoryPump.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -47,7 +47,7 @@ public void pumpDataEntries(DataEntryReader dataEntryReader) { if (!directory.exists()) { - throw new IOException("No such file or directory"); + throw new IOException("No such file or directory: " + directory); } readFiles(directory, dataEntryReader); @@ -78,7 +78,7 @@ private void readFiles(File file, DataEntryReader dataEntryReader) } catch (IOException e) { - throw (IOException)new IOException("Can't read ["+listedFile.getName()+"] ("+e.getMessage()+")").initCause(e); + throw new IOException("Can't read ["+listedFile.getName()+"] ("+e.getMessage()+")", e); } } } diff --git a/src/proguard/io/DirectoryWriter.java b/core/src/proguard/io/DirectoryWriter.java similarity index 53% rename from src/proguard/io/DirectoryWriter.java rename to core/src/proguard/io/DirectoryWriter.java index c4383ce7d..dfedcef7b 100644 --- a/src/proguard/io/DirectoryWriter.java +++ b/core/src/proguard/io/DirectoryWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,7 +24,6 @@ import java.io.*; - /** * This DataEntryWriter writes data entries to individual files in a given * directory. @@ -36,10 +35,6 @@ 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. @@ -57,13 +52,6 @@ public DirectoryWriter(File baseFile, 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()) @@ -75,54 +63,41 @@ public boolean createDirectory(DataEntry dataEntry) throws IOException } - public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) + throws IOException { - return getOutputStream(dataEntry, null); + return getFile(dataEntry1).equals(getFile(dataEntry2)); } - public OutputStream getOutputStream(DataEntry dataEntry, - Finisher finisher) throws IOException + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException { File file = getFile(dataEntry); - // Should we close the current file? - if (!isFile && - currentFile != null && - !currentFile.equals(file)) + // Make sure the parent directories exist. + File parentDirectory = file.getParentFile(); + if (parentDirectory != null && + !parentDirectory.exists() && + !parentDirectory.mkdirs()) { - closeEntry(); + throw new IOException("Can't create directory [" + parentDirectory.getPath() + "]"); } - // 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; + return + new BufferedOutputStream( + new FileOutputStream(file)); } public void close() throws IOException { - // Close the file stream, if any. - closeEntry(); + } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "DirectoryWriter (base " + (isFile?"file ":"directory") + " ["+baseFile+"])"); } @@ -140,26 +115,4 @@ private File getFile(DataEntry dataEntry) dataEntry.getName().replace(ClassConstants.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/core/src/proguard/io/ExtraDataEntryWriter.java b/core/src/proguard/io/ExtraDataEntryWriter.java new file mode 100644 index 000000000..fcfd9b32a --- /dev/null +++ b/core/src/proguard/io/ExtraDataEntryWriter.java @@ -0,0 +1,182 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.MultiValueMap; + +import java.io.*; +import java.util.*; + +/** + * This DataEntryWriter writes out all data entries to a delegate + * DataEntryWriter, inserting additional data entries that are attached + * to the written data entry. The output stream of the additional data + * entry is not used. + * + * @author Eric Lafortune + */ +public class ExtraDataEntryWriter implements DataEntryWriter +{ + private final MultiValueMap extraEntryNameMap; + private final Set extraEntryNamesWritten = new HashSet(); + + private final DataEntryWriter dataEntryWriter; + private final DataEntryWriter extraDataEntryWriter; + + private final String entrySuffix; + + /** + * Creates a new ExtraDataEntryWriter that writes one given extra data entry + * together with the first data entry that is written. + * + * @param extraEntryName the name of the extra data entry. + * @param dataEntryWriter the writer to which the entries are + * written, including the extra data entry. + */ + public ExtraDataEntryWriter(String extraEntryName, + DataEntryWriter dataEntryWriter) + { + this(extraEntryName, dataEntryWriter, dataEntryWriter); + } + + + /** + * Creates a new ExtraDataEntryWriter that writes one given extra data entry + * together with the first data entry that is written. + * + * @param extraEntryName the name of the extra data entry. + * @param dataEntryWriter the writer to which the entries are + * written. + * @param extraDataEntryWriter the writer to which the extra data entry + * will be written. + */ + public ExtraDataEntryWriter(String extraEntryName, + DataEntryWriter dataEntryWriter, + DataEntryWriter extraDataEntryWriter) + { + this(new MultiValueMap(), + dataEntryWriter, + extraDataEntryWriter, + null); + extraEntryNameMap.put(null, extraEntryName); + } + + /** + * Creates a new ExtraDataEntryWriter. + * + * @param extraEntryNameMap a map with data entry names and their + * associated extra data entries. An extra + * data entry that is associated with multiple + * entries is only written once. + * @param dataEntryWriter the writer to which the entries are + * written. + * @param extraDataEntryWriter the writer to which the extra data entry + * will be written. + * @param entrySuffix an optional file suffix. It is stripped + * from the entry name when looking up the + * entry in the map, and added to all extra + * entry names. + */ + public ExtraDataEntryWriter(MultiValueMap extraEntryNameMap, + DataEntryWriter dataEntryWriter, + DataEntryWriter extraDataEntryWriter, + String entrySuffix ) + { + this.extraEntryNameMap = extraEntryNameMap; + this.dataEntryWriter = dataEntryWriter; + this.extraDataEntryWriter = extraDataEntryWriter; + this.entrySuffix = entrySuffix; + } + + + // Implementations for DataEntryWriter. + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + return dataEntryWriter.createDirectory(dataEntry); + } + + + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) + throws IOException + { + return dataEntryWriter.sameOutputStream(dataEntry1, dataEntry2); + } + + + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException + { + // Write all default extra entries. + writeExtraEntries(dataEntry, null); + + // Write all extra entries attached to the current data entry. + writeExtraEntries(dataEntry); + + // Delegate to write out the actual entry. + return dataEntryWriter.createOutputStream(dataEntry); + } + + + private void writeExtraEntries(DataEntry dataEntry) throws IOException + { + String mapKey = dataEntry.getName(); + if (entrySuffix != null && mapKey.endsWith(entrySuffix)) + { + mapKey = mapKey.substring(0, mapKey.length() - entrySuffix.length()); + } + + writeExtraEntries(dataEntry, mapKey); + } + + + private void writeExtraEntries(DataEntry dataEntry, + String key) throws IOException + { + Set extraEntryNames = extraEntryNameMap.get(key); + if (extraEntryNames != null) + { + for (String extraEntryName : extraEntryNames) + { + if (!extraEntryNamesWritten.contains(extraEntryName)) + { + String fullEntryName = entrySuffix != null ? extraEntryName + entrySuffix : extraEntryName; + RenamedDataEntry extraEntry = new RenamedDataEntry(dataEntry, fullEntryName); + extraDataEntryWriter.createOutputStream(extraEntry); + extraEntryNamesWritten.add(extraEntryName); + writeExtraEntries(extraEntry); + } + } + } + } + + public void close() throws IOException + { + dataEntryWriter.close(); + } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "ExtraDataEntryWriter"); + dataEntryWriter.println(pw, prefix + " "); + } +} diff --git a/src/proguard/io/FileDataEntry.java b/core/src/proguard/io/FileDataEntry.java similarity index 71% rename from src/proguard/io/FileDataEntry.java rename to core/src/proguard/io/FileDataEntry.java index cc3e85389..a53091069 100644 --- a/src/proguard/io/FileDataEntry.java +++ b/core/src/proguard/io/FileDataEntry.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -44,6 +44,17 @@ public FileDataEntry(File directory, } + /** + * Returns the complete file, including its directory. + */ + public File getFile() + { + return file.equals(directory) ? + file : + new File(directory, getRelativeFilePath()); + } + + // Implementations for DataEntry. public String getName() @@ -51,9 +62,33 @@ public String getName() // Chop the directory name from the file name and get the right separators. return file.equals(directory) ? file.getName() : + getRelativeFilePath(); + } + + + /** + * Returns the file path of this data entry, relative to the base directory. + * If the file equals the base directory, an empty string is returned. + */ + private String getRelativeFilePath() + { + return file.equals(directory) ? + "" : file.getPath() - .substring(directory.getPath().length() + File.separator.length()) - .replace(File.separatorChar, ClassConstants.PACKAGE_SEPARATOR); + .substring(directory.getPath().length() + File.separator.length()) + .replace(File.separatorChar, ClassConstants.PACKAGE_SEPARATOR); + } + + + public String getOriginalName() + { + return getName(); + } + + + public long getSize() + { + return file.length(); } diff --git a/src/proguard/io/FilteredDataEntryReader.java b/core/src/proguard/io/FilteredDataEntryReader.java similarity index 98% rename from src/proguard/io/FilteredDataEntryReader.java rename to core/src/proguard/io/FilteredDataEntryReader.java index c2449bdbe..f5426bb8d 100644 --- a/src/proguard/io/FilteredDataEntryReader.java +++ b/core/src/proguard/io/FilteredDataEntryReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/io/FilteredDataEntryWriter.java b/core/src/proguard/io/FilteredDataEntryWriter.java similarity index 78% rename from src/proguard/io/FilteredDataEntryWriter.java rename to core/src/proguard/io/FilteredDataEntryWriter.java index 7b0dfc9aa..59bcb5a8d 100644 --- a/src/proguard/io/FilteredDataEntryWriter.java +++ b/core/src/proguard/io/FilteredDataEntryWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -87,14 +87,19 @@ public boolean createDirectory(DataEntry dataEntry) throws IOException } - public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) + throws IOException { - return getOutputStream(dataEntry, null); + boolean accepts1 = dataEntryFilter.accepts(dataEntry1); + boolean accepts2 = dataEntryFilter.accepts(dataEntry2); + return + accepts1 ? !accepts2 || acceptedDataEntryWriter == null || acceptedDataEntryWriter.sameOutputStream(dataEntry1, dataEntry2) : + accepts2 || rejectedDataEntryWriter == null || rejectedDataEntryWriter.sameOutputStream(dataEntry1, dataEntry2); } - public OutputStream getOutputStream(DataEntry dataEntry, - Finisher finisher) throws IOException + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException { // Get the right data entry writer. DataEntryWriter dataEntryWriter = dataEntryFilter.accepts(dataEntry) ? @@ -103,7 +108,7 @@ public OutputStream getOutputStream(DataEntry dataEntry, // Delegate to it, if it's not null. return dataEntryWriter != null ? - dataEntryWriter.getOutputStream(dataEntry, finisher) : + dataEntryWriter.createOutputStream(dataEntry) : null; } @@ -122,4 +127,18 @@ public void close() throws IOException rejectedDataEntryWriter = null; } } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "FilteredDataEntryWriter (filter = "+dataEntryFilter+")"); + if (acceptedDataEntryWriter != null) + { + acceptedDataEntryWriter.println(pw, prefix + " "); + } + if (rejectedDataEntryWriter != null) + { + rejectedDataEntryWriter.println(pw, prefix + " "); + } + } } diff --git a/src/proguard/io/Finisher.java b/core/src/proguard/io/Finisher.java similarity index 95% rename from src/proguard/io/Finisher.java rename to core/src/proguard/io/Finisher.java index c92632ca2..52eaedf34 100644 --- a/src/proguard/io/Finisher.java +++ b/core/src/proguard/io/Finisher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/io/IdleRewriter.java b/core/src/proguard/io/IdleRewriter.java new file mode 100644 index 000000000..11e1ee219 --- /dev/null +++ b/core/src/proguard/io/IdleRewriter.java @@ -0,0 +1,51 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 reads data entries and requests their corresponding + * output streams from a given DataEntryWriter, without actually using the + * output stream. + * + * @author Eric Lafortune + */ +public class IdleRewriter implements DataEntryReader +{ + private final DataEntryWriter dataEntryWriter; + + + public IdleRewriter(DataEntryWriter dataEntryWriter) + { + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryReader. + + public void read(DataEntry dataEntry) throws IOException + { + // Get the output entry corresponding to this input entry, but don't + // even try to close it. + dataEntryWriter.createOutputStream(dataEntry); + } +} diff --git a/src/proguard/io/JarReader.java b/core/src/proguard/io/JarReader.java similarity index 82% rename from src/proguard/io/JarReader.java rename to core/src/proguard/io/JarReader.java index 718af32f6..89e03fa9d 100644 --- a/src/proguard/io/JarReader.java +++ b/core/src/proguard/io/JarReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -32,14 +32,26 @@ public class JarReader implements DataEntryReader { private final DataEntryReader dataEntryReader; + private final boolean jmod; /** - * Creates a new JarReader. + * Creates a new JarReader that doesn't read jmods. */ public JarReader(DataEntryReader dataEntryReader) + { + this(dataEntryReader, false); + } + + + /** + * Creates a new JarReader. + */ + public JarReader(DataEntryReader dataEntryReader, + boolean jmod) { this.dataEntryReader = dataEntryReader; + this.jmod = jmod; } @@ -47,6 +59,12 @@ public JarReader(DataEntryReader dataEntryReader) public void read(DataEntry dataEntry) throws IOException { + if (jmod) + { + // Eat the magic bytes + dataEntry.getInputStream().read(new byte[4]); + } + ZipInputStream zipInputStream = new ZipInputStream(dataEntry.getInputStream()); try diff --git a/core/src/proguard/io/JarWriter.java b/core/src/proguard/io/JarWriter.java new file mode 100644 index 000000000..02ca763aa --- /dev/null +++ b/core/src/proguard/io/JarWriter.java @@ -0,0 +1,219 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.Date; + +/** + * This DataEntryWriter sends data entries to a given jar/zip file. + * + * @author Eric Lafortune + */ +public class JarWriter implements DataEntryWriter +{ + private final byte[] header; + private final int modificationTime; + private final DataEntryWriter dataEntryWriter; + + private DataEntry currentParentEntry; + private ZipOutput currentZipOutput; + + + /** + * Creates a new JarWriter. + * @param dataEntryWriter the data entry writer that can provide + * output streams for the jar/zip archives. + */ + public JarWriter(DataEntryWriter dataEntryWriter) + { + this(null, dataEntryWriter); + } + + + /** + * Creates a new JarWriter. + * @param header an optional header for the jar file. + * @param dataEntryWriter the data entry writer that can provide + * output streams for the jar/zip archives. + */ + public JarWriter(byte[] header, + DataEntryWriter dataEntryWriter) + { + this(header, currentTime(), dataEntryWriter); + } + + + /** + * Creates a new JarWriter. + * @param header an optional header for the jar file. + * @param modificationTime the modification date and time of the zip + * entries, in DOS format. + * @param dataEntryWriter the data entry writer that can provide + * output streams for the jar/zip archives. + */ + public JarWriter(byte[] header, + int modificationTime, + DataEntryWriter dataEntryWriter) + { + this.header = header; + this.modificationTime = modificationTime; + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryWriter. + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + finishIfNecessary(dataEntry); + setUp(dataEntry); + + // Did we get a zip output? + if (currentZipOutput == null) + { + return false; + } + + // Get the directory entry name. + String name = dataEntry.getName() + ClassConstants.PACKAGE_SEPARATOR; + + // Create a new directory entry. + OutputStream outputStream = + currentZipOutput.createOutputStream(name, + false, + modificationTime); + outputStream.close(); + + return true; + } + + + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) + throws IOException + { + return dataEntry1 != null && + dataEntry2 != null && + dataEntry1.getName().equals(dataEntry2.getName()) && + dataEntryWriter.sameOutputStream(dataEntry1.getParent(), + dataEntry2.getParent()); + } + + + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException + { + finishIfNecessary(dataEntry); + setUp(dataEntry); + + // Did we get a zip output? + if (currentZipOutput == null) + { + return null; + } + + // Create a new zip entry. + return currentZipOutput.createOutputStream(dataEntry.getName(), + true, + modificationTime); + } + + + public void close() throws IOException + { + finish(); + + // Close the delegate writer. + dataEntryWriter.close(); + } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "JarWriter"); + dataEntryWriter.println(pw, prefix + " "); + } + + + // Small utility methods. + + /** + * Sets up the zip output for the given parent entry. + */ + protected void setUp(DataEntry dataEntry) throws IOException + { + if (currentZipOutput == null) + { + // Create a new zip output. + currentParentEntry = dataEntry.getParent(); + currentZipOutput = new ZipOutput(dataEntryWriter.createOutputStream(currentParentEntry), + header, + null, + 1); + } + } + + + private void finishIfNecessary(DataEntry dataEntry) throws IOException + { + // Would the new data entry end up in a different jar? + if (currentParentEntry != null && + !dataEntryWriter.sameOutputStream(currentParentEntry, dataEntry.getParent())) + { + finish(); + } + } + + + /** + * Closes the zip output, if any. + */ + protected void finish() throws IOException + { + // Finish the zip output, if any. + if (currentZipOutput != null) + { + // Close the zip output and its underlying output stream. + currentZipOutput.close(); + + currentParentEntry = null; + currentZipOutput = null; + } + } + + + /** + * Returns the current time in DOS format. + */ + private static int currentTime() + { + // Convert the current time into DOS date and time. + Date currentDate = new Date(); + return + (currentDate.getYear() - 80) << 25 | + (currentDate.getMonth() + 1) << 21 | + currentDate.getDate() << 16 | + currentDate.getHours() << 11 | + currentDate.getMinutes() << 5 | + currentDate.getSeconds() >> 1; + }} diff --git a/src/proguard/io/ManifestRewriter.java b/core/src/proguard/io/ManifestRewriter.java similarity index 96% rename from src/proguard/io/ManifestRewriter.java rename to core/src/proguard/io/ManifestRewriter.java index 1d790649f..7fbe315ee 100644 --- a/src/proguard/io/ManifestRewriter.java +++ b/core/src/proguard/io/ManifestRewriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,7 @@ import proguard.classfile.ClassPool; import java.io.*; +import java.nio.charset.Charset; /** * This DataEntryReader writes the manifest data entries that it reads to a @@ -37,9 +38,10 @@ public class ManifestRewriter extends DataEntryRewriter * Creates a new ManifestRewriter. */ public ManifestRewriter(ClassPool classPool, + Charset charset, DataEntryWriter dataEntryWriter) { - super(classPool, dataEntryWriter); + super(classPool, charset, dataEntryWriter); } diff --git a/src/proguard/io/NameFilter.java b/core/src/proguard/io/NameFilter.java similarity index 98% rename from src/proguard/io/NameFilter.java rename to core/src/proguard/io/NameFilter.java index e30606beb..f7caaeb81 100644 --- a/src/proguard/io/NameFilter.java +++ b/core/src/proguard/io/NameFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/io/NameFilteredDataEntryWriter.java b/core/src/proguard/io/NameFilteredDataEntryWriter.java new file mode 100644 index 000000000..b006c1553 --- /dev/null +++ b/core/src/proguard/io/NameFilteredDataEntryWriter.java @@ -0,0 +1,108 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 DataEntryWriter delegates to one of two other DataEntryWriter instances, + * depending on the name of the data entry. + * + * @author Eric Lafortune + */ +public class NameFilteredDataEntryWriter extends FilteredDataEntryWriter +{ + /** + * Creates a new NameFilteredDataEntryWriter that delegates to the given + * writer, depending on the given list of filters. + */ + public NameFilteredDataEntryWriter(String regularExpression, + DataEntryWriter acceptedDataEntryWriter) + { + this(regularExpression, acceptedDataEntryWriter, null); + } + + + /** + * Creates a new NameFilteredDataEntryWriter that delegates to either of + * the two given writers, depending on the given list of filters. + */ + public NameFilteredDataEntryWriter(String regularExpression, + DataEntryWriter acceptedDataEntryWriter, + DataEntryWriter rejectedDataEntryWriter) + { + this(new ListParser(new FileNameParser()).parse(regularExpression), + acceptedDataEntryWriter, + rejectedDataEntryWriter); + } + + + /** + * Creates a new NameFilteredDataEntryWriter that delegates to the given + * writer, depending on the given list of filters. + */ + public NameFilteredDataEntryWriter(List regularExpressions, + DataEntryWriter acceptedDataEntryWriter) + { + this(regularExpressions, acceptedDataEntryWriter, null); + } + + + /** + * Creates a new NameFilteredDataEntryWriter that delegates to either of + * the two given writers, depending on the given list of filters. + */ + public NameFilteredDataEntryWriter(List regularExpressions, + DataEntryWriter acceptedDataEntryWriter, + DataEntryWriter rejectedDataEntryWriter) + { + this(new ListParser(new FileNameParser()).parse(regularExpressions), + acceptedDataEntryWriter, + rejectedDataEntryWriter); + } + + + /** + * Creates a new NameFilteredDataEntryWriter that delegates to the given + * writer, depending on the given string matcher. + */ + public NameFilteredDataEntryWriter(StringMatcher stringMatcher, + DataEntryWriter acceptedDataEntryWriter) + { + this(stringMatcher, acceptedDataEntryWriter, null); + } + + + /** + * Creates a new NameFilteredDataEntryWriter that delegates to either of + * the two given writers, depending on the given string matcher. + */ + public NameFilteredDataEntryWriter(StringMatcher stringMatcher, + DataEntryWriter acceptedDataEntryWriter, + DataEntryWriter rejectedDataEntryWriter) + { + super(new DataEntryNameFilter(stringMatcher), + acceptedDataEntryWriter, + rejectedDataEntryWriter); + } +} \ No newline at end of file diff --git a/src/proguard/io/ParentDataEntryWriter.java b/core/src/proguard/io/ParentDataEntryWriter.java similarity index 70% rename from src/proguard/io/ParentDataEntryWriter.java rename to core/src/proguard/io/ParentDataEntryWriter.java index 8590296b4..1d0663513 100644 --- a/src/proguard/io/ParentDataEntryWriter.java +++ b/core/src/proguard/io/ParentDataEntryWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -46,24 +46,24 @@ public ParentDataEntryWriter(DataEntryWriter dataEntryWriter) // Implementations for DataEntryWriter. - public boolean createDirectory(DataEntry dataEntry) throws IOException { - return getOutputStream(dataEntry) != null; + return dataEntryWriter.createDirectory(dataEntry.getParent()); } - public OutputStream getOutputStream(DataEntry dataEntry) throws IOException + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) + throws IOException { - return getOutputStream(dataEntry, null); + return dataEntryWriter.sameOutputStream(dataEntry1.getParent(), + dataEntry2.getParent()); } - public OutputStream getOutputStream(DataEntry dataEntry, - Finisher finisher) throws IOException + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException { - return dataEntryWriter.getOutputStream(dataEntry.getParent(), - finisher); + return dataEntryWriter.createOutputStream(dataEntry.getParent()); } @@ -72,4 +72,11 @@ public void close() throws IOException dataEntryWriter.close(); dataEntryWriter = null; } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "ParentDataEntryWriter"); + dataEntryWriter.println(pw, prefix + " "); + } } diff --git a/core/src/proguard/io/PrefixAddingDataEntryWriter.java b/core/src/proguard/io/PrefixAddingDataEntryWriter.java new file mode 100644 index 000000000..2afdfbf1a --- /dev/null +++ b/core/src/proguard/io/PrefixAddingDataEntryWriter.java @@ -0,0 +1,95 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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, each time + * adding a prefix of the written data entry name. + * + * @author Eric Lafortune + */ +public class PrefixAddingDataEntryWriter implements DataEntryWriter +{ + private final String prefix; + private final DataEntryWriter dataEntryWriter; + + + /** + * Creates a new PrefixAddingDataEntryWriter. + */ + public PrefixAddingDataEntryWriter(String prefix, + DataEntryWriter dataEntryWriter) + { + this.prefix = prefix; + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryWriter. + + public boolean createDirectory(DataEntry dataEntry) + throws IOException + { + return dataEntryWriter.createDirectory(renamedDataEntry(dataEntry)); + } + + + public boolean sameOutputStream(DataEntry dataEntry1, + DataEntry dataEntry2) + throws IOException + { + return dataEntryWriter.sameOutputStream(renamedDataEntry(dataEntry1), + renamedDataEntry(dataEntry2)); + } + + + public OutputStream createOutputStream(DataEntry dataEntry) + throws IOException + { + return dataEntryWriter.createOutputStream(renamedDataEntry(dataEntry)); + } + + + public void close() throws IOException + { + dataEntryWriter.close(); + } + + + public void println(PrintWriter pw, String prefix) + { + pw.println(prefix + "PrefixAddingDataEntryWriter (prefix = "+prefix+")"); + dataEntryWriter.println(pw, prefix + " "); + } + + + // Small utility methods. + + /** + * Adds the prefix to the given data entry name. + */ + private DataEntry renamedDataEntry(DataEntry dataEntry) + { + return new RenamedDataEntry(dataEntry, prefix + dataEntry.getName()); + } +} diff --git a/core/src/proguard/io/PrefixStrippingDataEntryReader.java b/core/src/proguard/io/PrefixStrippingDataEntryReader.java new file mode 100644 index 000000000..8fdfd1f8b --- /dev/null +++ b/core/src/proguard/io/PrefixStrippingDataEntryReader.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.ArrayUtil; + +import java.io.IOException; + +/** + * This DataEntryReader delegates to a given DataEntryReader, each time + * stripping a possible prefix from the read data entry name. + * + * @author Eric Lafortune + */ +public class PrefixStrippingDataEntryReader implements DataEntryReader +{ + private final String prefix; + private final DataEntryReader dataEntryReader; + + + /** + * Creates a new PrefixStrippingDataEntryReader. + */ + public PrefixStrippingDataEntryReader(String prefix, + DataEntryReader dataEntryReader) + { + this.prefix = prefix; + this.dataEntryReader = dataEntryReader; + } + + + // Implementation for DataEntryReader. + + public void read(DataEntry dataEntry) throws IOException + { + // Strip the prefix if necessary. + String name = dataEntry.getName(); + if (name.startsWith(prefix)) + { + dataEntry = new RenamedDataEntry(dataEntry, + name.substring(prefix.length())); + } + + // Read the data entry. + dataEntryReader.read(dataEntry); + } +} diff --git a/src/proguard/io/RenamedDataEntry.java b/core/src/proguard/io/RenamedDataEntry.java similarity index 66% rename from src/proguard/io/RenamedDataEntry.java rename to core/src/proguard/io/RenamedDataEntry.java index 3ac3f1872..238e59368 100644 --- a/src/proguard/io/RenamedDataEntry.java +++ b/core/src/proguard/io/RenamedDataEntry.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,25 +20,22 @@ */ 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 +public class RenamedDataEntry extends WrappedDataEntry { - private final DataEntry dataEntry; private final String name; public RenamedDataEntry(DataEntry dataEntry, String name) { - this.dataEntry = dataEntry; - this.name = name; + super(dataEntry); + this.name = name; } @@ -50,34 +47,10 @@ public String getName() } - 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; + return name + " == " + wrappedEntry; } } diff --git a/src/proguard/io/DataEntryRenamer.java b/core/src/proguard/io/RenamedDataEntryReader.java similarity index 71% rename from src/proguard/io/DataEntryRenamer.java rename to core/src/proguard/io/RenamedDataEntryReader.java index 386fdfcb4..b537deccc 100644 --- a/src/proguard/io/DataEntryRenamer.java +++ b/core/src/proguard/io/RenamedDataEntryReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -32,41 +32,41 @@ * * @author Eric Lafortune */ -public class DataEntryRenamer implements DataEntryReader +public class RenamedDataEntryReader implements DataEntryReader { private final Map nameMap; - private final DataEntryReader renamedDataEntryReader; + private final DataEntryReader dataEntryReader; 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. + * Creates a new RenamedDataEntryReader. + * @param nameMap the map from old names to new names. + * @param dataEntryReader the DataEntryReader to which renamed data + * entries will be passed. */ - public DataEntryRenamer(Map nameMap, - DataEntryReader renamedDataEntryReader) + public RenamedDataEntryReader(Map nameMap, + DataEntryReader dataEntryReader) { - this(nameMap, renamedDataEntryReader, null); + this(nameMap, dataEntryReader, null); } /** - * Creates a new DataEntryRenamer. + * Creates a new RenamedDataEntryReader. * @param nameMap the map from old names to new names. - * @param renamedDataEntryReader the DataEntryReader to which renamed data + * @param dataEntryReader 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) + public RenamedDataEntryReader(Map nameMap, + DataEntryReader dataEntryReader, + DataEntryReader missingDataEntryReader) { this.nameMap = nameMap; - this.renamedDataEntryReader = renamedDataEntryReader; + this.dataEntryReader = dataEntryReader; this.missingDataEntryReader = missingDataEntryReader; } @@ -94,7 +94,7 @@ public void read(DataEntry dataEntry) throws IOException newName = newName.substring(0, newName.length() - 1); } - renamedDataEntryReader.read(new RenamedDataEntry(dataEntry, newName)); + dataEntryReader.read(new RenamedDataEntry(dataEntry, newName)); } else if (missingDataEntryReader != null) { diff --git a/src/proguard/io/DataEntryObfuscator.java b/core/src/proguard/io/RenamedDataEntryWriter.java similarity index 76% rename from src/proguard/io/DataEntryObfuscator.java rename to core/src/proguard/io/RenamedDataEntryWriter.java index 05a02156c..cbd2dbc97 100644 --- a/src/proguard/io/DataEntryObfuscator.java +++ b/core/src/proguard/io/RenamedDataEntryWriter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,50 +23,75 @@ import proguard.classfile.*; import proguard.classfile.util.ClassUtil; -import java.io.IOException; +import java.io.*; import java.util.Map; /** - * This DataEntryReader delegates to another DataEntryReader, renaming the + * This DataEntryWriter delegates to another DataEntryWriter, renaming the * data entries based on the renamed classes in the given ClassPool. * * @author Eric Lafortune */ -public class DataEntryObfuscator implements DataEntryReader +public class RenamedDataEntryWriter implements DataEntryWriter { private final ClassPool classPool; private final Map packagePrefixMap; - private final DataEntryReader dataEntryReader; + private final DataEntryWriter dataEntryWriter; /** - * Creates a new DataEntryObfuscator. + * Creates a new RenamedDataEntryWriter. * @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 + * @param dataEntryWriter the DataEntryWriter to which calls will be * delegated. */ - public DataEntryObfuscator(ClassPool classPool, - Map packagePrefixMap, - DataEntryReader dataEntryReader) + public RenamedDataEntryWriter(ClassPool classPool, + Map packagePrefixMap, + DataEntryWriter dataEntryWriter) { this.classPool = classPool; this.packagePrefixMap = packagePrefixMap; - this.dataEntryReader = dataEntryReader; + this.dataEntryWriter = dataEntryWriter; } - // Implementations for DataEntryReader. + // Implementations for DataEntryWriter. - public void read(DataEntry dataEntry) throws IOException + public boolean createDirectory(DataEntry dataEntry) throws IOException { - // Delegate to the actual data entry reader. - dataEntryReader.read(renamedDataEntry(dataEntry)); + return dataEntryWriter.createDirectory(renamedDataEntry(dataEntry)); } + public boolean sameOutputStream(DataEntry dataEntry1, DataEntry dataEntry2) throws IOException + { + return dataEntryWriter.sameOutputStream(dataEntry1, dataEntry2); + } + + + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException + { + return dataEntryWriter.createOutputStream(renamedDataEntry(dataEntry)); + } + + + public void close() throws IOException + { + dataEntryWriter.close(); + } + + + public void println(PrintWriter pw, String prefix) + { + dataEntryWriter.println(pw, prefix); + } + + + // Small utility methods. + /** * Create a renamed data entry, if possible. */ diff --git a/core/src/proguard/io/RenamedParentDataEntryWriter.java b/core/src/proguard/io/RenamedParentDataEntryWriter.java new file mode 100644 index 000000000..b4e725605 --- /dev/null +++ b/core/src/proguard/io/RenamedParentDataEntryWriter.java @@ -0,0 +1,116 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; + +import java.io.*; + +/** + * This DataEntryWriter delegates to another DataEntryWriter, renaming + * parent data entries based on the given matcher. + * + * @author Thomas Neidhart + */ +public class RenamedParentDataEntryWriter implements DataEntryWriter +{ + private final StringMatcher matcher; + private final String newParentName; + private final DataEntryWriter dataEntryWriter; + + + /** + * Creates a new RenamedParentDataEntryWriter. + * + * @param matcher the string matcher to match parent entries. + * @param newParentName the new parent name to use. + * @param dataEntryWriter the DataEntryWriter to which the writing will + * be delegated. + */ + public RenamedParentDataEntryWriter(StringMatcher matcher, + String newParentName, + DataEntryWriter dataEntryWriter) + { + this.matcher = matcher; + this.newParentName = newParentName; + this.dataEntryWriter = dataEntryWriter; + } + + + // Implementations for DataEntryWriter. + + public boolean createDirectory(DataEntry dataEntry) throws IOException + { + return dataEntryWriter.createDirectory(getRedirectedEntry(dataEntry)); + } + + + public boolean sameOutputStream(DataEntry dataEntry1, DataEntry dataEntry2) + throws IOException + { + return dataEntryWriter.sameOutputStream(getRedirectedEntry(dataEntry1), + getRedirectedEntry(dataEntry2)); + } + + + public OutputStream createOutputStream(DataEntry dataEntry) throws IOException + { + return dataEntryWriter.createOutputStream(getRedirectedEntry(dataEntry)); + } + + + public void close() throws IOException + { + dataEntryWriter.close(); + } + + + public void println(PrintWriter pw, String prefix) + { + dataEntryWriter.println(pw, prefix); + } + + private DataEntry getRedirectedEntry(DataEntry dataEntry) + { + if (dataEntry == null) + { + return null; + } + + final DataEntry parentEntry = dataEntry.getParent(); + if (parentEntry != null && + matcher.matches(parentEntry.getName())) + { + final DataEntry renamedParentEntry = + new RenamedDataEntry(parentEntry, newParentName); + + return new WrappedDataEntry(dataEntry) { + public DataEntry getParent() + { + return renamedParentEntry; + } + }; + } + + return dataEntry; + } + +} diff --git a/core/src/proguard/io/WrappedDataEntry.java b/core/src/proguard/io/WrappedDataEntry.java new file mode 100644 index 000000000..d084c1049 --- /dev/null +++ b/core/src/proguard/io/WrappedDataEntry.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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. + * + * @author Thomas Neidhart + */ +public class WrappedDataEntry implements DataEntry +{ + protected final DataEntry wrappedEntry; + + + public WrappedDataEntry(DataEntry wrappedEntry) + { + this.wrappedEntry = wrappedEntry; + } + + + public void closeInputStream() throws IOException + { + wrappedEntry.closeInputStream(); + } + + public String getName() + { + return wrappedEntry.getName(); + } + + + public String getOriginalName() + { + return wrappedEntry.getOriginalName(); + } + + + public long getSize() + { + return wrappedEntry.getSize(); + } + + + public boolean isDirectory() + { + return wrappedEntry.isDirectory(); + } + + + public InputStream getInputStream() throws IOException + { + return wrappedEntry.getInputStream(); + } + + + public DataEntry getParent() + { + return wrappedEntry.getParent(); + } + + + public String toString() + { + return getName(); + } + +} diff --git a/src/proguard/io/ZipDataEntry.java b/core/src/proguard/io/ZipDataEntry.java similarity index 93% rename from src/proguard/io/ZipDataEntry.java rename to core/src/proguard/io/ZipDataEntry.java index 7425745b3..a1dcba36f 100644 --- a/src/proguard/io/ZipDataEntry.java +++ b/core/src/proguard/io/ZipDataEntry.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -65,6 +65,18 @@ public String getName() } + public String getOriginalName() + { + return getName(); + } + + + public long getSize() + { + return zipEntry.getSize(); + } + + public boolean isDirectory() { return zipEntry.isDirectory(); diff --git a/core/src/proguard/io/ZipFileDataEntry.java b/core/src/proguard/io/ZipFileDataEntry.java new file mode 100644 index 000000000..6ad4d5380 --- /dev/null +++ b/core/src/proguard/io/ZipFileDataEntry.java @@ -0,0 +1,123 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 ZipFileDataEntry implements DataEntry +{ + private final DataEntry parent; + private final ZipEntry zipEntry; + private ZipFile zipFile; + private InputStream zipInputStream; + private InputStream bufferedInputStream; + + + public ZipFileDataEntry(DataEntry parent, + ZipEntry zipEntry, + ZipFile zipFile) + { + this.parent = parent; + this.zipEntry = zipEntry; + this.zipFile = zipFile; + } + + + // Implementations for DataEntry. + + public String getName() + { + // Get the right separators. + String name = zipEntry.getName() + .replace(File.separatorChar, ClassConstants.PACKAGE_SEPARATOR); + + // Chop the trailing directory slash, if any. + int length = name.length(); + return length > 0 && + name.charAt(length-1) == ClassConstants.PACKAGE_SEPARATOR ? + name.substring(0, length -1) : + name; + } + + + public String getOriginalName() + { + return getName(); + } + + + public long getSize() + { + return zipEntry.getSize(); + } + + + public boolean isDirectory() + { + return zipEntry.isDirectory(); + } + + + public InputStream getInputStream() throws IOException + { + if (zipInputStream == null) + { + zipInputStream = zipFile.getInputStream(zipEntry); + } + + if (bufferedInputStream == null) + { + bufferedInputStream = new BufferedInputStream(zipInputStream); + } + + return bufferedInputStream; + } + + + public void closeInputStream() throws IOException + { + zipInputStream.close(); + zipFile = null; + bufferedInputStream = null; + } + + + public DataEntry getParent() + { + return parent; + } + + + // Implementations for Object. + + public String toString() + { + return parent.toString() + ':' + getName(); + } +} diff --git a/core/src/proguard/io/ZipOutput.java b/core/src/proguard/io/ZipOutput.java new file mode 100644 index 000000000..d8bb94f7e --- /dev/null +++ b/core/src/proguard/io/ZipOutput.java @@ -0,0 +1,577 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.StringUtil; + +import java.io.*; +import java.util.*; +import java.util.zip.*; + +/** + * This class writes zip data to a given output stream. It returns a new + * output stream for each zip entry that is opened. An entry can be compressed + * or uncompressed. Uncompressed entries can be aligned to a multiple of a + * given number of bytes. + * + * Multiple entries and output streams can be open at the same time. The entries + * are added to the central directory in the order in which they are opened, but + * the corresponding data are only written when their output streams are closed. + * + * The code automatically computes the CRC and lengths of the data, for + * compressed and uncompressed data. + * + * @author Eric Lafortune + */ +public class ZipOutput +{ + private static final int MAGIC_LOCAL_FILE_HEADER = 0x04034b50; + private static final int MAGIC_CENTRAL_DIRECTORY_FILE_HEADER = 0x02014b50; + private static final int MAGIC_END_OF_CENTRAL_DIRECTORY = 0x06054b50; + + private static final int VERSION = 10; + private static final int GENERAL_PURPOSE_FLAG = 0; + private static final int METHOD_UNCOMPRESSED = 0; + private static final int METHOD_COMPRESSED = 8; + + private static final boolean DEBUG = false; + + + private DataOutputStream outputStream; + private final int uncompressedAlignment; + private final String comment; + + private List zipEntries = new ArrayList(); + private Set zipEntryNames = new HashSet(); + + private long centralDirectoryOffset; + + + /** + * Creates a new ZipOutput. + * @param outputStream the output stream to which the zip data will be + * written. + */ + public ZipOutput(OutputStream outputStream) + throws IOException + { + this(outputStream, null, null, 1); + } + + + /** + * Creates a new ZipOutput that aligns uncompressed entries. + * @param outputStream the output stream to which the zip data will + * be written. + * @param header an optional header for the jar file. + * @param comment optional comment for the entire zip file. + * @param uncompressedAlignment the requested alignment of uncompressed data. + */ + public ZipOutput(OutputStream outputStream, + byte[] header, + String comment, + int uncompressedAlignment) + throws IOException + { + this.outputStream = new DataOutputStream(outputStream); + this.comment = comment; + this.uncompressedAlignment = uncompressedAlignment; + + if (header != null) + { + outputStream.write(header); + } + } + + + /** + * Creates a new zip entry, returning an output stream to write its data. + * It is the caller's responsibility to close the output stream. + * @param name the name of the zip entry. + * @param compress specifies whether the entry should be compressed. + * @param modificationTime the modification date and time of the zip entry, + * in DOS format. + * @return an output stream for writing the data of the + * zip entry. + */ + public OutputStream createOutputStream(String name, + boolean compress, + int modificationTime) + throws IOException + { + return createOutputStream(name, + compress, + modificationTime, + null, + null); + } + + + /** + * Creates a new zip entry, returning an output stream to write its data. + * It is the caller's responsibility to close the output stream. + * @param name the name of the zip entry. + * @param compress specifies whether the entry should be compressed. + * @param modificationTime the modification date and time of the zip entry, + * in DOS format. + * @param extraField optional extra field data. These should contain + * chunks, each with a short ID, a short length + * (little endian), and their corresponding data. + * The IDs 0-31 are reserved for Pkware. + * Java's jar tool just specifies an ID 0xcafe on + * its first entry. + * @param comment optional comment. + * @return an output stream for writing the data of the + * zip entry. + */ + public OutputStream createOutputStream(String name, + boolean compress, + int modificationTime, + byte[] extraField, + String comment) + throws IOException + { + // Check if the name hasn't been used yet. + if (!zipEntryNames.add(name)) + { + throw new IOException("Duplicate jar entry ["+name+"]"); + } + + ZipEntry entry = new ZipEntry(name, + compress, + modificationTime, + extraField, + comment); + + // Add the entry to the list that will be put in the central directory. + zipEntries.add(entry); + + return entry.createOutputStream(); + } + + + /** + * Closes the zip archive, also closing the underlying output stream. + */ + public void close() throws IOException + { + // Write the central directory. + writeStartOfCentralDirectory(); + + for (int index = 0; index < zipEntries.size(); index++) + { + ZipEntry entry = (ZipEntry)zipEntries.get(index); + + entry.writeCentralDirectoryFileHeader(); + } + + writeEndOfCentralDirectory(); + + // Close the underlying output stream. + outputStream.close(); + + // Make sure the archive can't be used any further. + outputStream = null; + zipEntries = null; + zipEntryNames = null; + } + + + /** + * Starts the central directory. + */ + private void writeStartOfCentralDirectory() + { + // The central directory as such doesn't have a header. + centralDirectoryOffset = outputStream.size(); + } + + + /** + * Ends the central directory. + */ + private void writeEndOfCentralDirectory() throws IOException + { + if (DEBUG) + { + System.out.println("ZipOutput.writeEndOfCentralDirectory ("+zipEntries.size()+" entries)"); + } + + // The size of the central directory, not counting this trailer. + long centralDirectorySize = outputStream.size() - centralDirectoryOffset; + + writeInt(MAGIC_END_OF_CENTRAL_DIRECTORY); + writeShort(0); // Number of this disk. + writeShort(0); // Number of disk with central directory. + writeShort(zipEntries.size()); // Number of records on this disk. + writeShort(zipEntries.size()); // Total number of records. + writeInt(centralDirectorySize); // Size of central directory, in bytes. + writeInt(centralDirectoryOffset); // Offset of central directory. + + if (comment == null) + { + // No comment. + writeShort(0); + } + else + { + // Comment length and comment. + byte[] commentBytes = StringUtil.getUtf8Bytes(comment); + writeShort(commentBytes.length); + outputStream.write(commentBytes); + } + } + + + /** + * This class represents a zip entry in its enclosing zip file. It can + * provide an output stream and write its headers and its data to the main + * zip output stream. In fact, it automatically writes its local header and + * data when the output stream is closed. + */ + private class ZipEntry + { + private boolean compressed; + private int modificationTime; + private int crc; + private long compressedSize; + private long uncompressedSize; + private long offset; + private String name; + private byte[] extraField; + private String comment; + + + /** + * Creates a new zip entry, returning output stream to write its data. + * It is the caller's responsibility to close the output stream. + * @param name the name of the zip entry. + * @param compressed specifies whether the entry should be + * compressed. + * @param modificationTime the modification date and time of the zip + * entry, in DOS format. + * @param extraField optional extra field data. These should + * contain chunks, each with a short ID, a short + * length (little endian), and their + * corresponding data. The IDs 0-31 are reserved + * for Pkware. Java's jar tool just specifies an + * ID 0xcafe on its first entry. + * @param comment optional comment. + * @return an output stream for writing the zip data. + */ + private ZipEntry(String name, + boolean compressed, + int modificationTime, + byte[] extraField, + String comment) + { + this.name = name; + this.compressed = compressed; + this.modificationTime = modificationTime; + this.extraField = extraField; + this.comment = comment; + } + + + public OutputStream createOutputStream() throws IOException + { + return compressed ? + (OutputStream)new CompressedZipEntryOutputStream() : + (OutputStream)new UncompressedZipEntryOutputStream(); + } + + + /** + * Writes the local file header, which precedes the data, to the main + * zip output stream. + */ + private void writeLocalFileHeader() throws IOException + { + if (DEBUG) + { + System.out.println("ZipOutput.writeLocalFileHeader ["+name+"] (compressed = "+compressed+", offset = "+offset+", "+compressedSize+"/"+uncompressedSize+" bytes)"); + } + + writeInt(MAGIC_LOCAL_FILE_HEADER); + writeShort(VERSION); + writeShort(GENERAL_PURPOSE_FLAG); + writeShort(compressed ? METHOD_COMPRESSED : METHOD_UNCOMPRESSED); + writeInt(modificationTime); + writeInt(crc); + writeInt(compressedSize); + writeInt(uncompressedSize); + + byte[] nameBytes = StringUtil.getUtf8Bytes(name); + int nameLength = nameBytes.length; + int extraFieldLength = extraField == null ? 0 : extraField.length; + + writeShort(nameLength); + writeShort(extraFieldLength); + + outputStream.write(nameBytes); + + if (extraField != null) + { + outputStream.write(extraField); + } + } + + + /** + * Writes the file header for the central directory to the main zip + * output stream. + */ + public void writeCentralDirectoryFileHeader() throws IOException + { + if (DEBUG) + { + System.out.println("ZipOutput.writeCentralDirectoryFileHeader ["+name+"] (compressed = "+compressed+", offset = "+offset+", "+compressedSize+"/"+uncompressedSize+" bytes)"); + } + + writeInt(MAGIC_CENTRAL_DIRECTORY_FILE_HEADER); + writeShort(VERSION); // Creation version. + writeShort(VERSION); // Extraction Version. + writeShort(GENERAL_PURPOSE_FLAG); + writeShort(compressed ? METHOD_COMPRESSED : METHOD_UNCOMPRESSED); + writeInt(modificationTime); + writeInt(crc); + writeInt(compressedSize); + writeInt(uncompressedSize); + + byte[] nameBytes = StringUtil.getUtf8Bytes(name); + byte[] commentBytes = comment == null ? null : + StringUtil.getUtf8Bytes(comment); + + writeShort(nameBytes.length); + writeShort(extraField == null ? 0 : extraField.length); + writeShort(commentBytes == null ? 0 : commentBytes.length); + writeShort(0); // Disk number of file start. + writeShort(0); // Internal file attributes. + writeInt(0); // External file attributes. + writeInt(offset); + outputStream.write(nameBytes); + if (extraField != null) + { + outputStream.write(extraField); + } + + if (commentBytes != null) + { + outputStream.write(commentBytes); + } + } + + + /** + * This OutputStream writes its uncompressed zip entry out to its zip + * output stream when it is closed. + */ + private class UncompressedZipEntryOutputStream extends ByteArrayOutputStream + { + private CRC32 crc32 = new CRC32(); + + + private UncompressedZipEntryOutputStream() + { + super(16 * 1024); + } + + + // Overridden methods for OutputStream. + + public void write(int b) + { + super.write(b); + + crc32.update(b); + } + + + //public void write(byte[] b) throws IOException + //{ + // // The super implementation delegates to the method below. + // super.write(b); + //} + + + public void write(byte[] b, int off, int len) + { + super.write(b, off, len); + + crc32.update(b, off, len); + } + + + public void close() throws IOException + { + super.close(); + + byte[] bytes = super.toByteArray(); + + offset = outputStream.size(); + crc = (int)crc32.getValue(); + compressedSize = bytes.length; + uncompressedSize = bytes.length; + + writeLocalFileHeader(); + outputStream.write(bytes); + } + } + + + /** + * This OutputStream writes its compressed zip entry out to its zip + * output stream when it is closed. + */ + private class CompressedZipEntryOutputStream extends DeflaterOutputStream + { + private CRC32 crc32 = new CRC32(); + + + private CompressedZipEntryOutputStream() + { + super(new ByteArrayOutputStream(16 * 1024), + new Deflater(Deflater.BEST_COMPRESSION, true), + 1024); + } + + + // Overridden methods for OutputStream. + + //public void write(int b) throws IOException + //{ + // // The super implementation delegates to the method below. + // super.write(b); + //} + // + // + //public void write(byte[] b) throws IOException + //{ + // // The super implementation delegates to the method below. + // super.write(b); + //} + + + public void write(byte[] b, int off, int len) throws IOException + { + super.write(b, off, len); + + crc32.update(b, off, len); + uncompressedSize += len; + } + + + public void close() throws IOException + { + // Make sure the memory is freed. [JDK-4797189] + super.finish(); + super.def.end(); + super.close(); + + ByteArrayOutputStream byteArrayOutputStream = + (ByteArrayOutputStream)super.out; + + byte[] compressedBytes = byteArrayOutputStream.toByteArray(); + + offset = outputStream.size(); + crc = (int)crc32.getValue(); + compressedSize = compressedBytes.length; + + writeLocalFileHeader(); + outputStream.write(compressedBytes); + } + } + } + + + // Small utility methods. + + /** + * Writes out a little-endian short value to the zip output stream. + */ + private void writeShort(int value) throws IOException + { + outputStream.write(value); + outputStream.write(value >>> 8); + } + + + /** + * Writes out a little-endian int value to the zip output stream. + */ + private void writeInt(int value) throws IOException + { + outputStream.write(value); + outputStream.write(value >>> 8); + outputStream.write(value >>> 16); + outputStream.write(value >>> 24); + } + + + /** + * Writes out a little-endian int value to the zip output stream. + */ + private void writeInt(long value) throws IOException + { + outputStream.write((int)value); + outputStream.write((int)(value >>> 8)); + outputStream.write((int)(value >>> 16)); + outputStream.write((int)(value >>> 24)); + } + + + /** + * Provides a simple test for this class, creating a zip file with the + * given name and a few aligned/compressed/uncompressed zip entries. + */ + public static void main(String[] args) + { + try + { + ZipOutput output = + new ZipOutput(new FileOutputStream(args[0]), null, "Main file comment", 4); + + PrintWriter printWriter1 = + new PrintWriter(output.createOutputStream("file1.txt", false, 0, new byte[] { 0x34, 0x12, 4, 0, 0x48, 0x65, 0x6c, 0x6c, 0x6f }, "Comment")); + printWriter1.println("This is file 1."); + printWriter1.println("Hello, world!"); + printWriter1.close(); + + PrintWriter printWriter2 = + new PrintWriter(output.createOutputStream("file2.txt", true, 0, null, "Another comment")); + printWriter2.println("This is file 2."); + printWriter2.println("Hello, world!"); + printWriter2.close(); + + PrintWriter printWriter3 = + new PrintWriter(output.createOutputStream("file3.txt", false, 0, null, "Last comment")); + printWriter3.println("This is file 3."); + printWriter3.println("Hello, world!"); + printWriter3.close(); + + output.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } +} diff --git a/src/proguard/io/package.html b/core/src/proguard/io/package.html similarity index 100% rename from src/proguard/io/package.html rename to core/src/proguard/io/package.html diff --git a/src/proguard/obfuscate/AttributeShrinker.java b/core/src/proguard/obfuscate/AttributeShrinker.java similarity index 98% rename from src/proguard/obfuscate/AttributeShrinker.java rename to core/src/proguard/obfuscate/AttributeShrinker.java index 1916797a3..fdc3d92e3 100644 --- a/src/proguard/obfuscate/AttributeShrinker.java +++ b/core/src/proguard/obfuscate/AttributeShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/AttributeUsageMarker.java b/core/src/proguard/obfuscate/AttributeUsageMarker.java similarity index 97% rename from src/proguard/obfuscate/AttributeUsageMarker.java rename to core/src/proguard/obfuscate/AttributeUsageMarker.java index 45b7fabd0..71179f007 100644 --- a/src/proguard/obfuscate/AttributeUsageMarker.java +++ b/core/src/proguard/obfuscate/AttributeUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/ClassObfuscator.java b/core/src/proguard/obfuscate/ClassObfuscator.java similarity index 99% rename from src/proguard/obfuscate/ClassObfuscator.java rename to core/src/proguard/obfuscate/ClassObfuscator.java index bafe1ffc1..accee7397 100644 --- a/src/proguard/obfuscate/ClassObfuscator.java +++ b/core/src/proguard/obfuscate/ClassObfuscator.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/ClassRenamer.java b/core/src/proguard/obfuscate/ClassRenamer.java similarity index 98% rename from src/proguard/obfuscate/ClassRenamer.java rename to core/src/proguard/obfuscate/ClassRenamer.java index 47a656898..e91b07480 100644 --- a/src/proguard/obfuscate/ClassRenamer.java +++ b/core/src/proguard/obfuscate/ClassRenamer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/DictionaryNameFactory.java b/core/src/proguard/obfuscate/DictionaryNameFactory.java similarity index 54% rename from src/proguard/obfuscate/DictionaryNameFactory.java rename to core/src/proguard/obfuscate/DictionaryNameFactory.java index 59fa69b58..4625d6c48 100644 --- a/src/proguard/obfuscate/DictionaryNameFactory.java +++ b/core/src/proguard/obfuscate/DictionaryNameFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,6 +21,7 @@ package proguard.obfuscate; import java.io.*; +import java.net.URL; import java.util.*; /** @@ -41,6 +42,40 @@ public class DictionaryNameFactory implements NameFactory private int index = 0; + /** + * Creates a new DictionaryNameFactory. + * @param url the URL 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(URL url, + NameFactory nameFactory) throws IOException + { + this(url, true, nameFactory); + } + + + /** + * Creates a new DictionaryNameFactory. + * @param url the URL from which the names can be read. + * @param validJavaIdentifiers specifies whether the produced names should + * be valid Java identifiers. + * @param nameFactory the name factory from which names will be + * retrieved if the list of read names has been + * exhausted. + */ + public DictionaryNameFactory(URL url, + boolean validJavaIdentifiers, + NameFactory nameFactory) throws IOException + { + this (new BufferedReader( + new InputStreamReader( + url.openStream(), "UTF-8")), + validJavaIdentifiers, + nameFactory); + } + + /** * Creates a new DictionaryNameFactory. * @param file the file from which the names can be read. @@ -49,12 +84,63 @@ public class DictionaryNameFactory implements NameFactory */ public DictionaryNameFactory(File file, NameFactory nameFactory) throws IOException + { + this(file, true, nameFactory); + } + + + /** + * Creates a new DictionaryNameFactory. + * @param file the file from which the names can be read. + * @param validJavaIdentifiers specifies whether the produced names should + * be valid Java identifiers. + * @param nameFactory the name factory from which names will be + * retrieved if the list of read names has been + * exhausted. + */ + public DictionaryNameFactory(File file, + boolean validJavaIdentifiers, + NameFactory nameFactory) throws IOException + { + this (new BufferedReader( + new InputStreamReader( + new FileInputStream(file), "UTF-8")), + validJavaIdentifiers, + nameFactory); + } + + + /** + * Creates a new DictionaryNameFactory. + * @param reader the reader from which the names can be read. The + * reader is closed at the end. + * @param nameFactory the name factory from which names will be retrieved + * if the list of read names has been exhausted. + */ + public DictionaryNameFactory(Reader reader, + NameFactory nameFactory) throws IOException + { + this(reader, true, nameFactory); + } + + + /** + * Creates a new DictionaryNameFactory. + * @param reader the reader from which the names can be read. + * The reader is closed at the end. + * @param validJavaIdentifiers specifies whether the produced names should + * be valid Java identifiers. + * @param nameFactory the name factory from which names will be + * retrieved if the list of read names has been + * exhausted. + */ + public DictionaryNameFactory(Reader reader, + boolean validJavaIdentifiers, + NameFactory nameFactory) throws IOException { this.names = new ArrayList(); this.nameFactory = nameFactory; - Reader reader = new FileReader(file); - try { StringBuffer buffer = new StringBuffer(); @@ -66,9 +152,13 @@ public DictionaryNameFactory(File file, // Is it a valid identifier character? if (c != -1 && - (buffer.length() == 0 ? - Character.isJavaIdentifierStart((char)c) : - Character.isJavaIdentifierPart((char)c))) + (validJavaIdentifiers ? + (buffer.length() == 0 ? + Character.isJavaIdentifierStart((char)c) : + Character.isJavaIdentifierPart((char)c)) : + (c != '\n' && + c != '\r' && + c != COMMENT_CHARACTER))) { // Append it to the current identifier. buffer.append((char)c); @@ -98,7 +188,7 @@ public DictionaryNameFactory(File file, { c = reader.read(); } - while (c != -1 && + while (c != -1 && c != '\n' && c != '\r'); } @@ -176,10 +266,17 @@ public static void main(String[] args) DictionaryNameFactory factory = new DictionaryNameFactory(new File(args[0]), new SimpleNameFactory()); + // For debugging, we're always using UTF-8 instead of the default + // character encoding, even for writing to the standard output. + PrintWriter out = + new PrintWriter(new OutputStreamWriter(System.out, "UTF-8")); + for (int counter = 0; counter < 50; counter++) { - System.out.println("["+factory.nextName()+"]"); + out.println("[" + factory.nextName() + "]"); } + + out.flush(); } catch (IOException ex) { diff --git a/src/proguard/obfuscate/MapCleaner.java b/core/src/proguard/obfuscate/MapCleaner.java similarity index 96% rename from src/proguard/obfuscate/MapCleaner.java rename to core/src/proguard/obfuscate/MapCleaner.java index 70bc4c33e..df333ba01 100644 --- a/src/proguard/obfuscate/MapCleaner.java +++ b/core/src/proguard/obfuscate/MapCleaner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MappingKeeper.java b/core/src/proguard/obfuscate/MappingKeeper.java similarity index 99% rename from src/proguard/obfuscate/MappingKeeper.java rename to core/src/proguard/obfuscate/MappingKeeper.java index 4025c1a21..3933906cc 100644 --- a/src/proguard/obfuscate/MappingKeeper.java +++ b/core/src/proguard/obfuscate/MappingKeeper.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MappingPrinter.java b/core/src/proguard/obfuscate/MappingPrinter.java similarity index 99% rename from src/proguard/obfuscate/MappingPrinter.java rename to core/src/proguard/obfuscate/MappingPrinter.java index 50f6e49bc..f056ce688 100644 --- a/src/proguard/obfuscate/MappingPrinter.java +++ b/core/src/proguard/obfuscate/MappingPrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MappingProcessor.java b/core/src/proguard/obfuscate/MappingProcessor.java similarity index 98% rename from src/proguard/obfuscate/MappingProcessor.java rename to core/src/proguard/obfuscate/MappingProcessor.java index d7494cad8..8658ff19e 100644 --- a/src/proguard/obfuscate/MappingProcessor.java +++ b/core/src/proguard/obfuscate/MappingProcessor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MappingReader.java b/core/src/proguard/obfuscate/MappingReader.java similarity index 99% rename from src/proguard/obfuscate/MappingReader.java rename to core/src/proguard/obfuscate/MappingReader.java index bb672bf9b..802532fd6 100644 --- a/src/proguard/obfuscate/MappingReader.java +++ b/core/src/proguard/obfuscate/MappingReader.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MemberNameCleaner.java b/core/src/proguard/obfuscate/MemberNameCleaner.java similarity index 97% rename from src/proguard/obfuscate/MemberNameCleaner.java rename to core/src/proguard/obfuscate/MemberNameCleaner.java index f069100e7..35e48b4b5 100644 --- a/src/proguard/obfuscate/MemberNameCleaner.java +++ b/core/src/proguard/obfuscate/MemberNameCleaner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MemberNameCollector.java b/core/src/proguard/obfuscate/MemberNameCollector.java similarity index 98% rename from src/proguard/obfuscate/MemberNameCollector.java rename to core/src/proguard/obfuscate/MemberNameCollector.java index fcb62772c..90c6115c8 100644 --- a/src/proguard/obfuscate/MemberNameCollector.java +++ b/core/src/proguard/obfuscate/MemberNameCollector.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MemberNameConflictFixer.java b/core/src/proguard/obfuscate/MemberNameConflictFixer.java similarity index 99% rename from src/proguard/obfuscate/MemberNameConflictFixer.java rename to core/src/proguard/obfuscate/MemberNameConflictFixer.java index 44a43cb01..a62495992 100644 --- a/src/proguard/obfuscate/MemberNameConflictFixer.java +++ b/core/src/proguard/obfuscate/MemberNameConflictFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MemberNameFilter.java b/core/src/proguard/obfuscate/MemberNameFilter.java similarity index 98% rename from src/proguard/obfuscate/MemberNameFilter.java rename to core/src/proguard/obfuscate/MemberNameFilter.java index a7791b946..0e381a851 100644 --- a/src/proguard/obfuscate/MemberNameFilter.java +++ b/core/src/proguard/obfuscate/MemberNameFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MemberObfuscator.java b/core/src/proguard/obfuscate/MemberObfuscator.java similarity index 99% rename from src/proguard/obfuscate/MemberObfuscator.java rename to core/src/proguard/obfuscate/MemberObfuscator.java index 792e79404..02093dac2 100644 --- a/src/proguard/obfuscate/MemberObfuscator.java +++ b/core/src/proguard/obfuscate/MemberObfuscator.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MemberSpecialNameFilter.java b/core/src/proguard/obfuscate/MemberSpecialNameFilter.java similarity index 98% rename from src/proguard/obfuscate/MemberSpecialNameFilter.java rename to core/src/proguard/obfuscate/MemberSpecialNameFilter.java index 99949e64d..7af71b742 100644 --- a/src/proguard/obfuscate/MemberSpecialNameFilter.java +++ b/core/src/proguard/obfuscate/MemberSpecialNameFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/MultiMappingProcessor.java b/core/src/proguard/obfuscate/MultiMappingProcessor.java similarity index 98% rename from src/proguard/obfuscate/MultiMappingProcessor.java rename to core/src/proguard/obfuscate/MultiMappingProcessor.java index 0c18ade6e..5e4f6ea1b 100644 --- a/src/proguard/obfuscate/MultiMappingProcessor.java +++ b/core/src/proguard/obfuscate/MultiMappingProcessor.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/NameFactory.java b/core/src/proguard/obfuscate/NameFactory.java similarity index 95% rename from src/proguard/obfuscate/NameFactory.java rename to core/src/proguard/obfuscate/NameFactory.java index dd5e459d2..dd307c1e2 100644 --- a/src/proguard/obfuscate/NameFactory.java +++ b/core/src/proguard/obfuscate/NameFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/NameFactoryResetter.java b/core/src/proguard/obfuscate/NameFactoryResetter.java similarity index 96% rename from src/proguard/obfuscate/NameFactoryResetter.java rename to core/src/proguard/obfuscate/NameFactoryResetter.java index 96a92bf7d..e2b982a64 100644 --- a/src/proguard/obfuscate/NameFactoryResetter.java +++ b/core/src/proguard/obfuscate/NameFactoryResetter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/NameMarker.java b/core/src/proguard/obfuscate/NameMarker.java similarity index 98% rename from src/proguard/obfuscate/NameMarker.java rename to core/src/proguard/obfuscate/NameMarker.java index 6afe300ef..a690299cc 100644 --- a/src/proguard/obfuscate/NameMarker.java +++ b/core/src/proguard/obfuscate/NameMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/NumericNameFactory.java b/core/src/proguard/obfuscate/NumericNameFactory.java similarity index 95% rename from src/proguard/obfuscate/NumericNameFactory.java rename to core/src/proguard/obfuscate/NumericNameFactory.java index d6609d3c4..62e663fc3 100644 --- a/src/proguard/obfuscate/NumericNameFactory.java +++ b/core/src/proguard/obfuscate/NumericNameFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/Obfuscator.java b/core/src/proguard/obfuscate/Obfuscator.java similarity index 89% rename from src/proguard/obfuscate/Obfuscator.java rename to core/src/proguard/obfuscate/Obfuscator.java index 4b1f56161..dd753b430 100644 --- a/src/proguard/obfuscate/Obfuscator.java +++ b/core/src/proguard/obfuscate/Obfuscator.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -84,12 +84,12 @@ public void execute(ClassPool programClassPool, // Create a visitor for marking the seeds. NameMarker nameMarker = new NameMarker(); ClassPoolVisitor classPoolvisitor = - ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep, - nameMarker, - nameMarker, - false, - false, - true); + new KeepClassSpecificationVisitorFactory(false, false, true) + .createClassPoolVisitor(configuration.keep, + nameMarker, + nameMarker, + nameMarker, + null); // Mark the seeds. programClassPool.accept(classPoolvisitor); libraryClassPool.accept(classPoolvisitor); @@ -98,15 +98,19 @@ public void execute(ClassPool programClassPool, libraryClassPool.classesAccept(nameMarker); libraryClassPool.classesAccept(new AllMemberVisitor(nameMarker)); - // We also keep the names of all methods of classes that are returned - // by dynamic method invocations. They may return dynamic - // implementations of interfaces. The method names then have to match - // with the invoke dynamic names. + // We also keep the names of the abstract methods of functional + // interfaces that are returned by dynamic method invocations. + // The functional method names then have to match the names + // in the dynamic method invocations with LambdaMetafactory. programClassPool.classesAccept( new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7, new AllConstantVisitor( new DynamicReturnedClassVisitor( - new AllMemberVisitor(nameMarker))))); + new FunctionalInterfaceFilter( + new ClassHierarchyTraveler(true, false, true, false, + new AllMethodVisitor( + new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, + nameMarker)))))))); // Mark attributes that have to be kept. AttributeVisitor attributeUsageMarker = @@ -200,11 +204,11 @@ public void execute(ClassPool programClassPool, // Come up with new names for all class members. NameFactory nameFactory = new SimpleNameFactory(); - if (configuration.obfuscationDictionary != null) { - nameFactory = new DictionaryNameFactory(configuration.obfuscationDictionary, - nameFactory); + nameFactory = + new DictionaryNameFactory(configuration.obfuscationDictionary, + nameFactory); } WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn); @@ -232,8 +236,7 @@ public void execute(ClassPool programClassPool, { // Come up with new names for all non-private class members. programClassPool.classesAccept( - new MultiClassVisitor(new ClassVisitor[] - { + new MultiClassVisitor( // Collect all private member names in this class and down // the hierarchy. new ClassHierarchyTraveler(true, false, false, true, @@ -242,7 +245,8 @@ public void execute(ClassPool programClassPool, new MemberNameCollector(configuration.overloadAggressively, descriptorMap)))), - // Collect all non-private member names anywhere in the hierarchy. + // Collect all non-private member names anywhere in the + // hierarchy. new ClassHierarchyTraveler(true, true, true, true, new AllMemberVisitor( new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, @@ -258,12 +262,11 @@ public void execute(ClassPool programClassPool, // Clear the collected names. new MapCleaner(descriptorMap) - })); + )); // Come up with new names for all private class members. programClassPool.classesAccept( - new MultiClassVisitor(new ClassVisitor[] - { + new MultiClassVisitor( // Collect all member names in this class. new AllMemberVisitor( new MemberNameCollector(configuration.overloadAggressively, @@ -289,6 +292,18 @@ public void execute(ClassPool programClassPool, new MemberNameCollector(configuration.overloadAggressively, descriptorMap))))), + // Collect all default method names from interfaces of + // any classes down the hierarchy. + // This is an extended version of the above problem + // (Sun/Oracle bug #802464, ProGuard bug #662, and + // ProGuard test #2060). + new ClassHierarchyTraveler(false, false, false, true, + new ClassHierarchyTraveler(false, false, true, false, + new AllMethodVisitor( + new MemberAccessFilter(0, ClassConstants.ACC_ABSTRACT | ClassConstants.ACC_STATIC, + new MemberNameCollector(configuration.overloadAggressively, + descriptorMap))))), + // Assign new names to all private members in this class. new AllMemberVisitor( new MemberAccessFilter(ClassConstants.ACC_PRIVATE, 0, @@ -298,7 +313,7 @@ public void execute(ClassPool programClassPool, // Clear the collected names. new MapCleaner(descriptorMap) - })); + )); } // Some class members may have ended up with conflicting names. @@ -324,8 +339,7 @@ public void execute(ClassPool programClassPool, // Replace conflicting non-private member names with special names. programClassPool.classesAccept( - new MultiClassVisitor(new ClassVisitor[] - { + new MultiClassVisitor( // Collect all private member names in this class and down // the hierarchy. new ClassHierarchyTraveler(true, false, false, true, @@ -356,13 +370,12 @@ public void execute(ClassPool programClassPool, // 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[] - { + new MultiClassVisitor( // Collect all member names in this class. new AllMemberVisitor( new MemberNameCollector(configuration.overloadAggressively, @@ -388,7 +401,7 @@ public void execute(ClassPool programClassPool, // Clear the collected names. new MapCleaner(descriptorMap) - })); + )); // Print out any warnings about member name conflicts. int warningCount = warningPrinter.getWarningCount(); @@ -434,6 +447,11 @@ public void execute(ClassPool programClassPool, } } + if (configuration.addConfigurationDebugging) + { + programClassPool.classesAccept(new RenamedFlagSetter()); + } + // Actually apply the new names. programClassPool.classesAccept(new ClassRenamer()); libraryClassPool.classesAccept(new ClassRenamer()); diff --git a/src/proguard/obfuscate/ParameterNameMarker.java b/core/src/proguard/obfuscate/ParameterNameMarker.java similarity index 98% rename from src/proguard/obfuscate/ParameterNameMarker.java rename to core/src/proguard/obfuscate/ParameterNameMarker.java index 6070715d3..49b201797 100644 --- a/src/proguard/obfuscate/ParameterNameMarker.java +++ b/core/src/proguard/obfuscate/ParameterNameMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/obfuscate/PrefixingNameFactory.java b/core/src/proguard/obfuscate/PrefixingNameFactory.java new file mode 100644 index 000000000..b135a945a --- /dev/null +++ b/core/src/proguard/obfuscate/PrefixingNameFactory.java @@ -0,0 +1,60 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; + + +/** + * NameFactory that prepends the names of the wrapped NameFactory with + * a fixed prefix. + * + * @author Johan Leys + */ +public class PrefixingNameFactory implements NameFactory +{ + private final NameFactory delegateNameFactory; + private final String prefix; + + + /** + * Creates a new PrefixingNameFactory. + * @param delegateNameFactory the wrapped NameFactory. + * @param prefix the prefix to add to all generated names. + */ + public PrefixingNameFactory(NameFactory delegateNameFactory, + String prefix) + { + this.delegateNameFactory = delegateNameFactory; + this.prefix = prefix; + } + + + // Implementations for NameFactory. + + public String nextName() + { + return prefix + delegateNameFactory.nextName(); + } + + public void reset() + { + delegateNameFactory.reset(); + } +} diff --git a/core/src/proguard/obfuscate/RenamedFlagSetter.java b/core/src/proguard/obfuscate/RenamedFlagSetter.java new file mode 100644 index 000000000..dc77cded7 --- /dev/null +++ b/core/src/proguard/obfuscate/RenamedFlagSetter.java @@ -0,0 +1,73 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.visitor.AttributeVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + + +/** + * This ClassVisitor sets the ACC_RENAMED flag for classes or class members + * that have been renamed. + * + * @author Johan Leys + */ +public class RenamedFlagSetter +extends SimplifiedVisitor +implements ClassVisitor, + + // Implementation interfaces. + MemberVisitor, + AttributeVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + String oldName = programClass.getName(); + String newName = ClassObfuscator.newClassName(programClass); + + if (newName != null && !oldName.equals(newName)) + { + programClass.u2accessFlags |= ClassConstants.ACC_RENAMED; + } + + // Print out the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + String oldName = programMember.getName(programClass); + String newName = MemberObfuscator.newMemberName(programMember); + + if (newName != null && !newName.equals(oldName)) + { + programMember.u2accessFlags |= ClassConstants.ACC_RENAMED; + } + } +} diff --git a/src/proguard/obfuscate/SimpleNameFactory.java b/core/src/proguard/obfuscate/SimpleNameFactory.java similarity index 98% rename from src/proguard/obfuscate/SimpleNameFactory.java rename to core/src/proguard/obfuscate/SimpleNameFactory.java index 1dc9d62e6..b0b5af713 100644 --- a/src/proguard/obfuscate/SimpleNameFactory.java +++ b/core/src/proguard/obfuscate/SimpleNameFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/SourceFileRenamer.java b/core/src/proguard/obfuscate/SourceFileRenamer.java similarity index 98% rename from src/proguard/obfuscate/SourceFileRenamer.java rename to core/src/proguard/obfuscate/SourceFileRenamer.java index 7e2d8d700..c9a4572c2 100644 --- a/src/proguard/obfuscate/SourceFileRenamer.java +++ b/core/src/proguard/obfuscate/SourceFileRenamer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/obfuscate/SpecialNameFactory.java b/core/src/proguard/obfuscate/SpecialNameFactory.java similarity index 97% rename from src/proguard/obfuscate/SpecialNameFactory.java rename to core/src/proguard/obfuscate/SpecialNameFactory.java index 0e7f56be8..c421a2bc3 100644 --- a/src/proguard/obfuscate/SpecialNameFactory.java +++ b/core/src/proguard/obfuscate/SpecialNameFactory.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/obfuscate/UniqueMemberNameFactory.java b/core/src/proguard/obfuscate/UniqueMemberNameFactory.java new file mode 100644 index 000000000..01a0361ac --- /dev/null +++ b/core/src/proguard/obfuscate/UniqueMemberNameFactory.java @@ -0,0 +1,93 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.Clazz; + +/** + * NameFactory which only generates names that don't exist yet as members + * on the class for which it is created. + * + * @author Johan Leys + */ +public class UniqueMemberNameFactory implements NameFactory +{ + private static final String INJECTED_MEMBER_PREFIX = "$$"; + + private final NameFactory delegateNameFactory; + private final Clazz clazz; + + + /** + * Utility for creating a new NameFactory that can generate names for injected + * members: the generated names are unique within the given class, and don't + * clash with non-injected members of its super classes. + * + * @param clazz the class for which to generate a NameFactory. + * @return the new NameFactory instance. + */ + public static UniqueMemberNameFactory newInjectedMemberNameFactory(Clazz clazz) + { + return new UniqueMemberNameFactory( + new PrefixingNameFactory( + new SimpleNameFactory(), INJECTED_MEMBER_PREFIX), clazz); + } + + + /** + * Creates a new UniqueMemberNameFactory. + * @param delegateNameFactory the delegate NameFactory, used for generating + * new candidate names. + * @param clazz the class in which to check for existing + * member names. + */ + public UniqueMemberNameFactory(NameFactory delegateNameFactory, + Clazz clazz) + { + this.delegateNameFactory = delegateNameFactory; + this.clazz = clazz; + } + + + // Implementations for NameFactory. + + public String nextName() + { + String name; + + // Check if the name doesn't exist yet. We don't have additional + // descriptor information, so we can only search on the name. + do + { + name = delegateNameFactory.nextName(); + } + while (clazz.findField(name, null) != null || + clazz.findMethod(name, null) != null); + + return name; + } + + + public void reset() + { + delegateNameFactory.reset(); + } +} \ No newline at end of file diff --git a/src/proguard/obfuscate/package.html b/core/src/proguard/obfuscate/package.html similarity index 100% rename from src/proguard/obfuscate/package.html rename to core/src/proguard/obfuscate/package.html diff --git a/src/proguard/optimize/BootstrapMethodArgumentShrinker.java b/core/src/proguard/optimize/BootstrapMethodArgumentShrinker.java similarity index 98% rename from src/proguard/optimize/BootstrapMethodArgumentShrinker.java rename to core/src/proguard/optimize/BootstrapMethodArgumentShrinker.java index 5507ed9e1..cd094dbaa 100644 --- a/src/proguard/optimize/BootstrapMethodArgumentShrinker.java +++ b/core/src/proguard/optimize/BootstrapMethodArgumentShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/ChangedCodePrinter.java b/core/src/proguard/optimize/ChangedCodePrinter.java similarity index 95% rename from src/proguard/optimize/ChangedCodePrinter.java rename to core/src/proguard/optimize/ChangedCodePrinter.java index 71efd0290..c3fe243c8 100644 --- a/src/proguard/optimize/ChangedCodePrinter.java +++ b/core/src/proguard/optimize/ChangedCodePrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,7 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.module.*; import proguard.classfile.attribute.preverification.*; import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.util.ClassUtil; @@ -83,6 +84,24 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + attributeVisitor.visitModuleAttribute(clazz, moduleAttribute); + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + attributeVisitor.visitModuleMainClassAttribute(clazz, moduleMainClassAttribute); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + attributeVisitor.visitModulePackagesAttribute(clazz, modulePackagesAttribute); + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { attributeVisitor.visitDeprecatedAttribute(clazz, deprecatedAttribute); diff --git a/src/proguard/optimize/ConstantMemberFilter.java b/core/src/proguard/optimize/ConstantMemberFilter.java similarity index 97% rename from src/proguard/optimize/ConstantMemberFilter.java rename to core/src/proguard/optimize/ConstantMemberFilter.java index 640ddc015..ebe324657 100644 --- a/src/proguard/optimize/ConstantMemberFilter.java +++ b/core/src/proguard/optimize/ConstantMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/ConstantParameterFilter.java b/core/src/proguard/optimize/ConstantParameterFilter.java similarity index 97% rename from src/proguard/optimize/ConstantParameterFilter.java rename to core/src/proguard/optimize/ConstantParameterFilter.java index af16d9a47..6009cfcb5 100644 --- a/src/proguard/optimize/ConstantParameterFilter.java +++ b/core/src/proguard/optimize/ConstantParameterFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/DuplicateInitializerFixer.java b/core/src/proguard/optimize/DuplicateInitializerFixer.java similarity index 84% rename from src/proguard/optimize/DuplicateInitializerFixer.java rename to core/src/proguard/optimize/DuplicateInitializerFixer.java index a25534390..82153554f 100644 --- a/src/proguard/optimize/DuplicateInitializerFixer.java +++ b/core/src/proguard/optimize/DuplicateInitializerFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,6 +27,7 @@ import proguard.classfile.editor.ConstantPoolEditor; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; +import proguard.optimize.info.*; /** * This MemberVisitor adds an additional parameter to the duplicate @@ -37,7 +38,11 @@ public class DuplicateInitializerFixer implements MemberVisitor, AttributeVisitor { + //* private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("dif") != null; + //*/ private static final char[] TYPES = new char[] { @@ -89,6 +94,8 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM if (KeepMarker.isKept(programMethod)) { // Fix the other initializer. + // We'll just proceed if it is being kept as well; + // apparently the descriptor types didn't matter so much. programMethod = (ProgramMethod)similarMethod; } @@ -132,6 +139,25 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM programMethod.attributesAccept(programClass, this); + // Update the optimization info. + MethodOptimizationInfo methodOptimizationInfo = + ProgramMethodOptimizationInfo.getMethodOptimizationInfo(programMethod); + if (methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ProgramMethodOptimizationInfo programMethodOptimizationInfo = + (ProgramMethodOptimizationInfo)methodOptimizationInfo; + + int parameterCount = + ClassUtil.internalMethodParameterCount(newDescriptor, + programMethod.getAccessFlags()); + programMethodOptimizationInfo.insertParameter(parameterCount - 1); + + int parameterSize = + programMethodOptimizationInfo.getParameterSize(); + programMethodOptimizationInfo.setParameterSize(parameterSize + 1); + programMethodOptimizationInfo.setParameterUsed(parameterSize); + } + // Visit the initializer, if required. if (extraFixedInitializerVisitor != null) { @@ -212,4 +238,4 @@ public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, Pa parameterAnnotationsAttribute.parameterAnnotations = annotations; } } -} \ No newline at end of file +} diff --git a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java b/core/src/proguard/optimize/DuplicateInitializerInvocationFixer.java similarity index 99% rename from src/proguard/optimize/DuplicateInitializerInvocationFixer.java rename to core/src/proguard/optimize/DuplicateInitializerInvocationFixer.java index a080208e1..c4acbe249 100644 --- a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java +++ b/core/src/proguard/optimize/DuplicateInitializerInvocationFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/optimize/KeepMarker.java b/core/src/proguard/optimize/KeepMarker.java new file mode 100644 index 000000000..af5ab7c3c --- /dev/null +++ b/core/src/proguard/optimize/KeepMarker.java @@ -0,0 +1,134 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; +import proguard.optimize.info.*; + +/** + * This ClassVisitor, MemberVisitor and + * AttributeVisitor marks classes, class members and + * code attributes it visits. The marked elements will remain + * unchanged as necessary in the optimization step. + * + * @see NoSideEffectMethodMarker + * @author Eric Lafortune + */ +public class KeepMarker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + ClassOptimizationInfo.setClassOptimizationInfo(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + ClassOptimizationInfo.setClassOptimizationInfo(libraryClass); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + FieldOptimizationInfo.setFieldOptimizationInfo(programClass, programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + MethodOptimizationInfo.setMethodOptimizationInfo(programClass, programMethod); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + FieldOptimizationInfo.setFieldOptimizationInfo(libraryClass, libraryField); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + MethodOptimizationInfo.setMethodOptimizationInfo(libraryClass, libraryMethod); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + CodeAttributeOptimizationInfo.setCodeAttributeOptimizationInfo(codeAttribute); + } + + + // Small utility methods. + + public static boolean isKept(Clazz clazz) + { + ClassOptimizationInfo info = + ClassOptimizationInfo.getClassOptimizationInfo(clazz); + + return info != null && + info.isKept(); + } + + public static boolean isKept(Field field) + { + FieldOptimizationInfo info = + FieldOptimizationInfo.getFieldOptimizationInfo(field); + + return info != null && + info.isKept(); + } + + public static boolean isKept(Method method) + { + MethodOptimizationInfo info = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + return info != null && + info.isKept(); + } + + public static boolean isKept(CodeAttribute codeAttribute) + { + CodeAttributeOptimizationInfo info = + CodeAttributeOptimizationInfo.getCodeAttributeOptimizationInfo(codeAttribute); + + return info != null && + info.isKept(); + } + +} diff --git a/core/src/proguard/optimize/KeptClassFilter.java b/core/src/proguard/optimize/KeptClassFilter.java new file mode 100644 index 000000000..93e8799b0 --- /dev/null +++ b/core/src/proguard/optimize/KeptClassFilter.java @@ -0,0 +1,94 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 its visits to one of two ClassVisitor's, + * depending on whether the visited class is kept or not. + * + * @see KeepMarker + * + * @author Eric Lafortune + */ +public class KeptClassFilter +implements ClassVisitor +{ + private final ClassVisitor acceptedVisitor; + private final ClassVisitor rejectedVisitor; + + + /** + * Creates a new KeptClassFilter. + * + * @param acceptedVisitor the class visitor to which accepted (kept) + * classes will be delegated. + */ + public KeptClassFilter(ClassVisitor acceptedVisitor) + { + this(acceptedVisitor, null); + } + + /** + * Creates a new KeptClassFilter. + * + * @param acceptedVisitor the class visitor to which accepted (kept) + * classes will be delegated. + * @param rejectedVisitor the class visitor to which rejected (unkept) + * classes will be delegated. + */ + public KeptClassFilter(ClassVisitor acceptedVisitor, + ClassVisitor rejectedVisitor) + { + this.acceptedVisitor = acceptedVisitor; + this.rejectedVisitor = rejectedVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + ClassVisitor delegateVisitor = selectVisitor(programClass); + if (delegateVisitor != null) { + delegateVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + ClassVisitor delegateVisitor = selectVisitor(libraryClass); + if (delegateVisitor != null) { + delegateVisitor.visitLibraryClass(libraryClass); + } + } + + + // Small utility methods. + + private ClassVisitor selectVisitor(Clazz clazz) + { + return KeepMarker.isKept(clazz) ? acceptedVisitor : rejectedVisitor; + } +} \ No newline at end of file diff --git a/src/proguard/optimize/KeptMemberFilter.java b/core/src/proguard/optimize/KeptMemberFilter.java similarity index 97% rename from src/proguard/optimize/KeptMemberFilter.java rename to core/src/proguard/optimize/KeptMemberFilter.java index 7c8eb7cc5..7ce731173 100644 --- a/src/proguard/optimize/KeptMemberFilter.java +++ b/core/src/proguard/optimize/KeptMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/MemberDescriptorSpecializer.java b/core/src/proguard/optimize/MemberDescriptorSpecializer.java similarity index 98% rename from src/proguard/optimize/MemberDescriptorSpecializer.java rename to core/src/proguard/optimize/MemberDescriptorSpecializer.java index ed3a6ecdf..6438d9c79 100644 --- a/src/proguard/optimize/MemberDescriptorSpecializer.java +++ b/core/src/proguard/optimize/MemberDescriptorSpecializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/MethodDescriptorShrinker.java b/core/src/proguard/optimize/MethodDescriptorShrinker.java similarity index 97% rename from src/proguard/optimize/MethodDescriptorShrinker.java rename to core/src/proguard/optimize/MethodDescriptorShrinker.java index b68877f02..99de95a43 100644 --- a/src/proguard/optimize/MethodDescriptorShrinker.java +++ b/core/src/proguard/optimize/MethodDescriptorShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,9 +28,6 @@ import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; import proguard.optimize.info.*; -import proguard.optimize.peephole.VariableShrinker; - -import java.util.Arrays; /** * This MemberVisitor removes unused parameters in the descriptors of the @@ -47,7 +44,11 @@ public class MethodDescriptorShrinker implements MemberVisitor, AttributeVisitor { + //* private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("mds") != null; + //*/ private final MemberVisitor extraMemberVisitor; @@ -201,7 +202,7 @@ public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, Pa annotationIndex++; - parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; + parameterIndex += ClassUtil.internalTypeSize(type); } // Update the number of parameters. @@ -252,7 +253,7 @@ else if (DEBUG) System.out.println(" Deleting parameter #"+parameterIndex+" ["+type+"]"); } - parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; + parameterIndex += ClassUtil.internalTypeSize(type); } // Copy the return type. @@ -317,7 +318,7 @@ private Clazz[] shrinkReferencedClasses(Method method, referencedClassIndex += count; } - parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; + parameterIndex += ClassUtil.internalTypeSize(type); } // Copy the return type. diff --git a/src/proguard/optimize/MethodStaticizer.java b/core/src/proguard/optimize/MethodStaticizer.java similarity index 93% rename from src/proguard/optimize/MethodStaticizer.java rename to core/src/proguard/optimize/MethodStaticizer.java index 0d87e6255..b92e04c4a 100644 --- a/src/proguard/optimize/MethodStaticizer.java +++ b/core/src/proguard/optimize/MethodStaticizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,7 +21,6 @@ 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; @@ -39,8 +38,7 @@ */ public class MethodStaticizer extends SimplifiedVisitor -implements MemberVisitor, - AttributeVisitor +implements MemberVisitor { private final MemberVisitor extraStaticMemberVisitor; diff --git a/src/proguard/optimize/OptimizationInfoClassFilter.java b/core/src/proguard/optimize/OptimizationInfoClassFilter.java similarity index 83% rename from src/proguard/optimize/OptimizationInfoClassFilter.java rename to core/src/proguard/optimize/OptimizationInfoClassFilter.java index 93bf62eb6..83dad933e 100644 --- a/src/proguard/optimize/OptimizationInfoClassFilter.java +++ b/core/src/proguard/optimize/OptimizationInfoClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,15 +21,16 @@ package proguard.optimize; import proguard.classfile.*; -import proguard.classfile.visitor.*; -import proguard.optimize.info.ClassOptimizationInfo; +import proguard.classfile.visitor.ClassVisitor; +import proguard.optimize.info.*; /** * This ClassVisitor delegates its visits to another given - * ClassVisitor, but only when the visited class has optimization - * info. + * ClassVisitor, but only when the visited class has editable + * optimization info. * * @see ClassOptimizationInfo + * @see ProgramClassOptimizationInfo * @author Eric Lafortune */ public class OptimizationInfoClassFilter @@ -40,8 +41,8 @@ public class OptimizationInfoClassFilter /** * Creates a new OptimizationInfoClassFilter. - * @param classVisitor the ClassVisitor to which visits - * will be delegated. + * @param classVisitor the ClassVisitor to which visits will + * be delegated. */ public OptimizationInfoClassFilter(ClassVisitor classVisitor) { @@ -51,10 +52,11 @@ public OptimizationInfoClassFilter(ClassVisitor classVisitor) // Implementations for ClassVisitor. + public void visitProgramClass(ProgramClass programClass) { - // Does the class have optimization info? - if (ClassOptimizationInfo.getClassOptimizationInfo(programClass) != null) + // Does the field have optimization info? + if (ClassOptimizationInfo.getClassOptimizationInfo(programClass) instanceof ProgramClassOptimizationInfo) { classVisitor.visitProgramClass(programClass); } @@ -64,7 +66,7 @@ public void visitProgramClass(ProgramClass programClass) public void visitLibraryClass(LibraryClass libraryClass) { // Does the class have optimization info? - if (ClassOptimizationInfo.getClassOptimizationInfo(libraryClass) != null) + if (ClassOptimizationInfo.getClassOptimizationInfo(libraryClass) instanceof ProgramClassOptimizationInfo) { classVisitor.visitLibraryClass(libraryClass); } diff --git a/src/proguard/optimize/OptimizationInfoMemberFilter.java b/core/src/proguard/optimize/OptimizationInfoMemberFilter.java similarity index 55% rename from src/proguard/optimize/OptimizationInfoMemberFilter.java rename to core/src/proguard/optimize/OptimizationInfoMemberFilter.java index 950fcfa5b..1f90763c1 100644 --- a/src/proguard/optimize/OptimizationInfoMemberFilter.java +++ b/core/src/proguard/optimize/OptimizationInfoMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,17 +26,20 @@ /** * This MemberVisitor delegates its visits to another given - * MemberVisitor, but only when the visited member has optimization - * info. + * MemberVisitor, but only when the visited member has editable + * optimization info. * * @see FieldOptimizationInfo + * @see ProgramFieldOptimizationInfo * @see MethodOptimizationInfo + * @see ProgramMethodOptimizationInfo * @author Eric Lafortune */ public class OptimizationInfoMemberFilter implements MemberVisitor { private final MemberVisitor memberVisitor; + private final MemberVisitor otherMemberVisitor; /** @@ -46,48 +49,54 @@ public class OptimizationInfoMemberFilter */ public OptimizationInfoMemberFilter(MemberVisitor memberVisitor) { - this.memberVisitor = memberVisitor; + this(memberVisitor, null); } - // Implementations for MemberVisitor. - - public void visitProgramField(ProgramClass programClass, ProgramField programField) + /** + * Creates a new OptimizationInfoMemberFilter. + * @param memberVisitor the MemberVisitor to which visits will + * be delegated if the member has editable optimization + * info. + * @param otherMemberVisitor the MemberVisitor to which visits will + * be delegated if the member does not have editable + * optimization info. + */ + public OptimizationInfoMemberFilter(MemberVisitor memberVisitor, + MemberVisitor otherMemberVisitor) { - // Does the field have optimization info? - if (FieldOptimizationInfo.getFieldOptimizationInfo(programField) != null) - { - memberVisitor.visitProgramField(programClass, programField); - } + this.memberVisitor = memberVisitor; + this.otherMemberVisitor = otherMemberVisitor; } - public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) - { - // Does the field have optimization info? - if (FieldOptimizationInfo.getFieldOptimizationInfo(libraryField) != null) - { - memberVisitor.visitLibraryField(libraryClass, libraryField); - } - } + // Implementations for MemberVisitor. + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + public void visitProgramField(ProgramClass programClass, ProgramField programField) { - // Does the method have optimization info? - if (MethodOptimizationInfo.getMethodOptimizationInfo(programMethod) != null) + MemberVisitor visitor = + FieldOptimizationInfo.getFieldOptimizationInfo(programField) instanceof ProgramFieldOptimizationInfo ? + memberVisitor : otherMemberVisitor; + + if (visitor != null) { - memberVisitor.visitProgramMethod(programClass, programMethod); + visitor.visitProgramField(programClass, programField); } } - public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - // Does the method have optimization info? - if (MethodOptimizationInfo.getMethodOptimizationInfo(libraryMethod) != null) + MemberVisitor visitor = + MethodOptimizationInfo.getMethodOptimizationInfo(programMethod) instanceof ProgramMethodOptimizationInfo ? + memberVisitor : otherMemberVisitor; + + if (visitor != null) { - memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + visitor.visitProgramMethod(programClass, programMethod); } } } diff --git a/core/src/proguard/optimize/Optimizer.java b/core/src/proguard/optimize/Optimizer.java new file mode 100644 index 000000000..a0a1f735a --- /dev/null +++ b/core/src/proguard/optimize/Optimizer.java @@ -0,0 +1,1691 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.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 +{ + public static final boolean DETAILS = System.getProperty("optd") != null; + + private static final String CLASS_MARKING_FINAL = "class/marking/final"; + private static final String CLASS_UNBOXING_ENUM = "class/unboxing/enum"; + private static final String CLASS_MERGING_VERTICAL = "class/merging/vertical"; + private static final String CLASS_MERGING_HORIZONTAL = "class/merging/horizontal"; + private static final String CLASS_MERGING_WRAPPER = "class/merging/wrapper"; + 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_MARKING_SYNCHRONIZED = "method/marking/synchronized"; + 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_OBJECT = "code/simplification/object"; + private static final String CODE_SIMPLIFICATION_STRING = "code/simplification/string"; + private static final String CODE_SIMPLIFICATION_MATH = "code/simplification/math"; + 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_MARKING_SYNCHRONIZED, + 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_MATH, + CODE_SIMPLIFICATION_ADVANCED, + CODE_REMOVAL_ADVANCED, + CODE_REMOVAL_SIMPLE, + CODE_REMOVAL_VARIABLE, + CODE_REMOVAL_EXCEPTION, + CODE_ALLOCATION_VARIABLE, + }; + + + private final Configuration configuration; + + private final boolean classMarkingFinal; + private final boolean classUnboxingEnum; + private final boolean classMergingVertical; + private final boolean classMergingHorizontal; + private final boolean classMergingWrapper; + private final boolean fieldRemovalWriteonly; + private final boolean fieldMarkingPrivate; + private final boolean fieldPropagationValue; + private final boolean methodMarkingPrivate; + private final boolean methodMarkingStatic; + private final boolean methodMarkingFinal; + private final boolean methodMarkingSynchronized; + private final boolean methodRemovalParameter; + private final boolean methodPropagationParameter; + private final boolean methodPropagationReturnvalue; + private final boolean methodInliningShort; + private final boolean methodInliningUnique; + private final boolean methodInliningTailrecursion; + private final boolean codeMerging; + private final boolean codeSimplificationVariable; + private final boolean codeSimplificationArithmetic; + private final boolean codeSimplificationCast; + private final boolean codeSimplificationField; + private final boolean codeSimplificationBranch; + private final boolean codeSimplificationObject; + private final boolean codeSimplificationString; + private final boolean codeSimplificationMath; + private final boolean codeSimplificationPeephole; + private boolean codeSimplificationAdvanced; + private boolean codeRemovalAdvanced; + private boolean codeRemovalSimple; + private final boolean codeRemovalVariable; + private boolean codeRemovalException; + private final boolean codeAllocationVariable; + + + /** + * Creates a new Optimizer. + */ + public Optimizer(Configuration configuration) + { + this.configuration = configuration; + + // Create a matcher for filtering optimizations. + StringMatcher filter = configuration.optimizations != null ? + new ListParser(new NameParser()).parse(configuration.optimizations) : + new ConstantMatcher(true); + + classMarkingFinal = filter.matches(CLASS_MARKING_FINAL); + classUnboxingEnum = filter.matches(CLASS_UNBOXING_ENUM); + classMergingVertical = filter.matches(CLASS_MERGING_VERTICAL); + classMergingHorizontal = filter.matches(CLASS_MERGING_HORIZONTAL); + classMergingWrapper = filter.matches(CLASS_MERGING_WRAPPER); + fieldRemovalWriteonly = filter.matches(FIELD_REMOVAL_WRITEONLY); + fieldMarkingPrivate = filter.matches(FIELD_MARKING_PRIVATE); + fieldPropagationValue = filter.matches(FIELD_PROPAGATION_VALUE); + methodMarkingPrivate = filter.matches(METHOD_MARKING_PRIVATE); + methodMarkingStatic = filter.matches(METHOD_MARKING_STATIC); + methodMarkingFinal = filter.matches(METHOD_MARKING_FINAL); + methodMarkingSynchronized = filter.matches(METHOD_MARKING_SYNCHRONIZED); + methodRemovalParameter = filter.matches(METHOD_REMOVAL_PARAMETER); + methodPropagationParameter = filter.matches(METHOD_PROPAGATION_PARAMETER); + methodPropagationReturnvalue = filter.matches(METHOD_PROPAGATION_RETURNVALUE); + methodInliningShort = filter.matches(METHOD_INLINING_SHORT); + methodInliningUnique = filter.matches(METHOD_INLINING_UNIQUE); + methodInliningTailrecursion = filter.matches(METHOD_INLINING_TAILRECURSION); + codeMerging = filter.matches(CODE_MERGING); + codeSimplificationVariable = filter.matches(CODE_SIMPLIFICATION_VARIABLE); + codeSimplificationArithmetic = filter.matches(CODE_SIMPLIFICATION_ARITHMETIC); + codeSimplificationCast = filter.matches(CODE_SIMPLIFICATION_CAST); + codeSimplificationField = filter.matches(CODE_SIMPLIFICATION_FIELD); + codeSimplificationBranch = filter.matches(CODE_SIMPLIFICATION_BRANCH); + codeSimplificationObject = filter.matches(CODE_SIMPLIFICATION_OBJECT); + codeSimplificationString = filter.matches(CODE_SIMPLIFICATION_STRING); + codeSimplificationMath = filter.matches(CODE_SIMPLIFICATION_MATH); + codeSimplificationAdvanced = filter.matches(CODE_SIMPLIFICATION_ADVANCED); + codeRemovalAdvanced = filter.matches(CODE_REMOVAL_ADVANCED); + codeRemovalSimple = filter.matches(CODE_REMOVAL_SIMPLE); + codeRemovalVariable = filter.matches(CODE_REMOVAL_VARIABLE); + codeRemovalException = filter.matches(CODE_REMOVAL_EXCEPTION); + codeAllocationVariable = filter.matches(CODE_ALLOCATION_VARIABLE); + + // Some optimizations are required by other optimizations. + codeSimplificationAdvanced = + codeSimplificationAdvanced || + fieldPropagationValue || + methodPropagationParameter || + methodPropagationReturnvalue; + + codeRemovalAdvanced = + codeRemovalAdvanced || + fieldRemovalWriteonly || + methodMarkingStatic || + methodRemovalParameter; + + codeRemovalSimple = + codeRemovalSimple || + codeSimplificationBranch; + + codeRemovalException = + codeRemovalException || + codeRemovalAdvanced || + codeRemovalSimple; + + codeSimplificationPeephole = + codeSimplificationVariable || + codeSimplificationArithmetic || + codeSimplificationCast || + codeSimplificationField || + codeSimplificationBranch || + codeSimplificationObject || + codeSimplificationString || + codeSimplificationMath; + } + + + /** + * Performs optimization of the given program class pool. + */ + public boolean execute(final ClassPool programClassPool, + final ClassPool libraryClassPool, + final MultiValueMap injectedClassNameMap) 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 counters to count the numbers of optimizations. + final ClassCounter classMarkingFinalCounter = new ClassCounter(); + final ClassCounter classUnboxingEnumCounter = new ClassCounter(); + final ClassCounter classMergingVerticalCounter = new ClassCounter(); + final ClassCounter classMergingHorizontalCounter = new ClassCounter(); + final ClassCounter classMergingWrapperCounter = new ClassCounter(); + final MemberCounter fieldRemovalWriteonlyCounter = new MemberCounter(); + final MemberCounter fieldMarkingPrivateCounter = new MemberCounter(); + final MemberCounter fieldPropagationValueCounter = new MemberCounter(); + final MemberCounter methodMarkingPrivateCounter = new MemberCounter(); + final MemberCounter methodMarkingStaticCounter = new MemberCounter(); + final MemberCounter methodMarkingFinalCounter = new MemberCounter(); + final MemberCounter methodMarkingSynchronizedCounter = new MemberCounter(); + final MemberCounter methodRemovalParameterCounter1 = new MemberCounter(); + final MemberCounter methodRemovalParameterCounter2 = new MemberCounter(); + final MemberCounter methodPropagationParameterCounter = new MemberCounter(); + final MemberCounter methodPropagationReturnvalueCounter = new MemberCounter(); + final InstructionCounter methodInliningShortCounter = new InstructionCounter(); + final InstructionCounter methodInliningUniqueCounter = new InstructionCounter(); + final InstructionCounter methodInliningTailrecursionCounter = new InstructionCounter(); + final InstructionCounter codeMergingCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationVariableCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationArithmeticCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationCastCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationFieldCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationBranchCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationObjectCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationStringCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationMathCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationAndroidMathCounter = new InstructionCounter(); + final InstructionCounter codeSimplificationAdvancedCounter = new InstructionCounter(); + final InstructionCounter deletedCounter = new InstructionCounter(); + final InstructionCounter addedCounter = new InstructionCounter(); + final MemberCounter codeRemovalVariableCounter = new MemberCounter(); + final ExceptionCounter codeRemovalExceptionCounter = new ExceptionCounter(); + final MemberCounter codeAllocationVariableCounter = new MemberCounter(); + final MemberCounter initializerFixCounter1 = new MemberCounter(); + final MemberCounter initializerFixCounter2 = new MemberCounter(); + + // 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. + final KeepMarker keepMarker = new KeepMarker(); + ClassPoolVisitor classPoolvisitor = + new KeepClassSpecificationVisitorFactory(false, true, false) + .createClassPoolVisitor(configuration.keep, + keepMarker, + keepMarker, + keepMarker, + keepMarker); + // 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. + // We're not looking at enum classes though, so they can be simplified. + programClassPool.classesAccept( + new ClassAccessFilter(0, ClassConstants.ACC_ENUM, + 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.CLASS_VERSION_1_7, + new AllAttributeVisitor( + new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods, + new AllBootstrapMethodInfoVisitor( + new BootstrapMethodHandleTraveler( + new MethodrefTraveler( + new ReferencedMemberVisitor(keepMarker)))))))); + + // We also keep all bootstrap method arguments that point to methods. + // These arguments are typically the method handles for + // java.lang.invoke.LambdaMetafactory#metafactory, which provides the + // implementations for closures. + programClassPool.classesAccept( + new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7, + new AllAttributeVisitor( + new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods, + new AllBootstrapMethodInfoVisitor( + new BootstrapMethodArgumentVisitor( + new MethodrefTraveler( + new ReferencedMemberVisitor(keepMarker)))))))); + + // We also keep the classes and abstract methods of functional + // interfaces that are returned by dynamic method invocations. + // These functional interfaces have to remain suitable for the + // dynamic method invocations with LambdaMetafactory. + programClassPool.classesAccept( + new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7, + new AllConstantVisitor( + new DynamicReturnedClassVisitor( + new FunctionalInterfaceFilter( + new ClassHierarchyTraveler(true, false, true, false, + new MultiClassVisitor( + keepMarker, + new AllMethodVisitor( + new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, + keepMarker)) + ))))))); + + // Attach some optimization info to all classes and class members, so + // it can be filled out later. + programClassPool.classesAccept(new ProgramClassOptimizationInfoSetter()); + + programClassPool.classesAccept(new AllMemberVisitor( + new ProgramMemberOptimizationInfoSetter())); + + if (configuration.assumeNoSideEffects != null) + { + // Create a visitor for marking classes and methods that don't have + // any side effects. + NoSideEffectClassMarker noSideEffectClassMarker = new NoSideEffectClassMarker(); + NoSideEffectMethodMarker noSideEffectMethodMarker = new NoSideEffectMethodMarker(); + ClassPoolVisitor classPoolVisitor = + new ClassSpecificationVisitorFactory() + .createClassPoolVisitor(configuration.assumeNoSideEffects, + noSideEffectClassMarker, + noSideEffectMethodMarker); + + // Mark the seeds. + programClassPool.accept(classPoolVisitor); + libraryClassPool.accept(classPoolVisitor); + } + + if (configuration.assumeNoExternalSideEffects != null) + { + // Create a visitor for marking classes and methods that don't have + // any external side effects. + NoSideEffectClassMarker noSideEffectClassMarker = new NoSideEffectClassMarker(); + NoExternalSideEffectMethodMarker noSideEffectMethodMarker = new NoExternalSideEffectMethodMarker(); + ClassPoolVisitor classPoolVisitor = + new ClassSpecificationVisitorFactory() + .createClassPoolVisitor(configuration.assumeNoExternalSideEffects, + noSideEffectClassMarker, + noSideEffectMethodMarker); + + // Mark the seeds. + programClassPool.accept(classPoolVisitor); + libraryClassPool.accept(classPoolVisitor); + } + + if (configuration.assumeNoEscapingParameters != null) + { + // Create a visitor for marking methods that don't let any + // reference parameters escape. + NoEscapingParametersMethodMarker noEscapingParametersMethodMarker = new NoEscapingParametersMethodMarker(); + ClassPoolVisitor classPoolVisitor = + new ClassSpecificationVisitorFactory() + .createClassPoolVisitor(configuration.assumeNoEscapingParameters, + null, + noEscapingParametersMethodMarker); + + // Mark the seeds. + programClassPool.accept(classPoolVisitor); + libraryClassPool.accept(classPoolVisitor); + } + + if (configuration.assumeNoExternalReturnValues != null) + { + // Create a visitor for marking methods that don't let any + // reference parameters escape. + NoExternalReturnValuesMethodMarker noExternalReturnValuesMethodMarker = new NoExternalReturnValuesMethodMarker(); + ClassPoolVisitor classPoolVisitor = + new ClassSpecificationVisitorFactory() + .createClassPoolVisitor(configuration.assumeNoExternalReturnValues, + null, + noExternalReturnValuesMethodMarker); + + // Mark the seeds. + programClassPool.accept(classPoolVisitor); + libraryClassPool.accept(classPoolVisitor); + } + + if (classMarkingFinal) + { + // Make classes final, whereever possible. + programClassPool.classesAccept( + new ClassFinalizer(classMarkingFinalCounter)); + } + + if (methodMarkingFinal) + { + // Make methods final, whereever possible. + programClassPool.classesAccept( + new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE, + new AllMethodVisitor( + new MethodFinalizer(methodMarkingFinalCounter)))); + } + + // We'll repeatedly loop over all classes to mark read/write fields. + // side-effect methods, and escaping parameters. + final MutableBoolean repeatTrigger = new MutableBoolean(); + + // Create the various markers. + ReadWriteFieldMarker readWriteFieldMarker = + new ReadWriteFieldMarker(repeatTrigger); + + if (!fieldRemovalWriteonly) + { + // Mark all fields as read/write. + programClassPool.classesAccept( + new AllFieldVisitor( + readWriteFieldMarker)); + } + + SideEffectMethodMarker sideEffectMethodMarker = + new SideEffectMethodMarker(repeatTrigger); + + ParameterEscapeMarker parameterEscapeMarker = + new ParameterEscapeMarker(repeatTrigger); + + // Mark methods based on their headers. + programClassPool.classesAccept( + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + new MultiMemberVisitor( + sideEffectMethodMarker, + parameterEscapeMarker + )))); + + { + // Mark fields based on the method instructions in methods + // without editable optimization info. + programClassPool.accept( + new TimedClassPoolVisitor("Marking field usage in kept methods", + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + null, + + // member has no editable optimization info + new AllAttributeVisitor( + new AllInstructionVisitor( + readWriteFieldMarker)))))); + + ParallelAllClassVisitor.ClassVisitorFactory markingClassVisitor = + new ParallelAllClassVisitor.ClassVisitorFactory() + { + public ClassVisitor createClassVisitor() + { + ReferenceTracingValueFactory referenceTracingValueFactory1 = + new ReferenceTracingValueFactory(new TypedReferenceValueFactory()); + + PartialEvaluator partialEvaluator = + new PartialEvaluator(referenceTracingValueFactory1, + new ParameterTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory1)), + false, + referenceTracingValueFactory1); + + InstructionUsageMarker instructionUsageMarker = + new InstructionUsageMarker(partialEvaluator, false); + + // Create the various markers. + ReadWriteFieldMarker readWriteFieldMarker = + new ReadWriteFieldMarker(repeatTrigger); + + SideEffectMethodMarker sideEffectMethodMarker = + new SideEffectMethodMarker(repeatTrigger); + + ParameterEscapeMarker parameterEscapeMarker = + new ParameterEscapeMarker(repeatTrigger, partialEvaluator, false); + + return + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + new AllAttributeVisitor( + new DebugAttributeVisitor("Marking fields, methods, and parameters", + new MultiAttributeVisitor( + partialEvaluator, + parameterEscapeMarker, + instructionUsageMarker, + new AllInstructionVisitor( + instructionUsageMarker.necessaryInstructionFilter( + new MultiInstructionVisitor( + readWriteFieldMarker, + sideEffectMethodMarker, + parameterEscapeMarker + ))) + ))))); + } + }; + + // Mark fields, methods and parameters based on the method instructions + // for methods that have editable optimization info. + programClassPool.accept( + new RepeatedClassPoolVisitor(repeatTrigger, + new TimedClassPoolVisitor("Marking fields, methods and parameters", + new ParallelAllClassVisitor( + markingClassVisitor)))); + } + + if (methodMarkingSynchronized) + { + // Mark all superclasses of escaping (kept) classes. + programClassPool.classesAccept( + new EscapingClassFilter( + new ClassHierarchyTraveler(false, true, true, false, + new EscapingClassMarker()))); + + ParallelAllClassVisitor.ClassVisitorFactory markingEscapingClassVisitor = + new ParallelAllClassVisitor.ClassVisitorFactory() + { + public ClassVisitor createClassVisitor() + { + return + new AllMethodVisitor( + new AllAttributeVisitor( + new EscapingClassMarker())); + } + }; + + // Mark classes that escape to the heap. + programClassPool.accept( + new TimedClassPoolVisitor("Marking escaping classes", + new ParallelAllClassVisitor( + markingEscapingClassVisitor))); + + // Desynchronize all non-static methods whose classes don't escape. + programClassPool.classesAccept( + new EscapingClassFilter(null, + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + new MemberAccessFilter(ClassConstants.ACC_SYNCHRONIZED, ClassConstants.ACC_STATIC, + new MultiMemberVisitor( + new MemberAccessFlagCleaner(ClassConstants.ACC_SYNCHRONIZED), + methodMarkingSynchronizedCounter + )))))); + } + + if (fieldRemovalWriteonly) + { + // Count the write-only fields. + programClassPool.classesAccept( + new AllFieldVisitor( + new WriteOnlyFieldFilter(fieldRemovalWriteonlyCounter))); + } + + if (classUnboxingEnum) + { + ClassCounter counter = new ClassCounter(); + + // Mark all final enums that qualify as simple enums. + programClassPool.classesAccept( + new ClassAccessFilter(ClassConstants.ACC_FINAL | + ClassConstants.ACC_ENUM, 0, + new OptimizationInfoClassFilter( + new SimpleEnumClassChecker()))); + + // Count the preliminary number of simple enums. + programClassPool.classesAccept( + new SimpleEnumFilter(counter)); + + // Only continue checking simple enums if there are any candidates. + if (counter.getCount() > 0) + { + // Unmark all simple enums that are explicitly used as objects. + programClassPool.classesAccept( + new SimpleEnumUseChecker()); + + // Unmark all simple enums that are used in descriptors of + // kept class members. Changing their names could upset + // the name parameters of invokedynamic instructions. + programClassPool.classesAccept( + new SimpleEnumFilter(null, + new AllMemberVisitor( + new KeptMemberFilter( + new MemberDescriptorReferencedClassVisitor( + new OptimizationInfoClassFilter( + new SimpleEnumMarker(false))))))); + + // Count the definitive number of simple enums. + programClassPool.classesAccept( + new SimpleEnumFilter(classUnboxingEnumCounter)); + + // Only start handling simple enums if there are any. + if (classUnboxingEnumCounter.getCount() > 0) + { + // Simplify the use of the enum classes in code. + programClassPool.accept( + new TimedClassPoolVisitor("Simplify use of simple enums", + new AllMethodVisitor( + new AllAttributeVisitor( + new SimpleEnumUseSimplifier())))); + + // Simplify the static initializers of simple enum classes. + programClassPool.classesAccept( + new SimpleEnumFilter( + new SimpleEnumClassSimplifier())); + + // Simplify the use of the enum classes in descriptors. + programClassPool.classesAccept( + new SimpleEnumDescriptorSimplifier()); + + // Update references to class members with simple enum classes. + programClassPool.classesAccept(new MemberReferenceFixer()); + } + } + } + + // Mark all used parameters, including the 'this' parameters. + ParallelAllClassVisitor.ClassVisitorFactory markingUsedParametersClassVisitor = + new ParallelAllClassVisitor.ClassVisitorFactory() + { + public ClassVisitor createClassVisitor() + { + return + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + new ParameterUsageMarker(!methodMarkingStatic, + !methodRemovalParameter))); + } + }; + + programClassPool.accept( + new TimedClassPoolVisitor("Marking used parameters", + new ParallelAllClassVisitor( + markingUsedParametersClassVisitor))); + + // Mark all parameters of referenced methods in methods whose code must + // be kept. This prevents shrinking of method descriptors which may not + // be propagated correctly otherwise. + programClassPool.accept( + new TimedClassPoolVisitor("Marking used parameters in kept code attributes", + new AllClassVisitor( + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + null, + + // visit all methods that are kept + new AllAttributeVisitor( + new OptimizationCodeAttributeFilter( + null, + + // visit all code attributes that are kept + new AllInstructionVisitor( + new InstructionConstantVisitor( + new ConstantTagFilter(new int[] { ClassConstants.CONSTANT_Methodref, + ClassConstants.CONSTANT_InterfaceMethodref }, + new ReferencedMemberVisitor( + new OptimizationInfoMemberFilter( + // Mark all parameters including "this" of referenced methods + new ParameterUsageMarker(true, true, false)))))))) + ))))); + +// 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, so they can be propagated. + if (fieldPropagationValue || + methodPropagationParameter || + methodPropagationReturnvalue || + classMergingWrapper) + { + // We'll create values to be stored with fields, method parameters, + // and return values. + ValueFactory valueFactory = new ParticularValueFactory(); + ValueFactory detailedValueFactory = new DetailedArrayValueFactory(); + + InvocationUnit storingInvocationUnit = + new StoringInvocationUnit(valueFactory, + fieldPropagationValue, + methodPropagationParameter || classMergingWrapper, + methodPropagationReturnvalue); + + // Evaluate synthetic classes in more detail, notably to propagate + // the arrays of the classes generated for enum switch statements. + programClassPool.classesAccept( + new ClassAccessFilter(ClassConstants.ACC_SYNTHETIC, 0, + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Filling out fields, method parameters, and return values in synthetic classes", + new PartialEvaluator(detailedValueFactory, storingInvocationUnit, false)))))); + + // Evaluate non-synthetic classes. We may need to evaluate all + // casts, to account for downcasts when specializing descriptors. + + ParallelAllClassVisitor.ClassVisitorFactory fillingOutValuesClassVisitor = + new ParallelAllClassVisitor.ClassVisitorFactory() + { + public ClassVisitor createClassVisitor() + { + ValueFactory valueFactory = new ParticularValueFactory(); + + InvocationUnit storingInvocationUnit = + new StoringInvocationUnit(valueFactory, + fieldPropagationValue, + methodPropagationParameter || classMergingWrapper, + methodPropagationReturnvalue); + + return + new ClassAccessFilter(0, ClassConstants.ACC_SYNTHETIC, + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Filling out fields, method parameters, and return values", + new PartialEvaluator(valueFactory, storingInvocationUnit, + false))))); + } + }; + + programClassPool.accept( + new TimedClassPoolVisitor("Filling out values in non-synthetic classes", + new ParallelAllClassVisitor( + fillingOutValuesClassVisitor))); + + 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))); + } + + if (classUnboxingEnumCounter.getCount() > 0) + { + // Propagate the simple enum constant counts. + programClassPool.classesAccept( + new SimpleEnumFilter( + new SimpleEnumArrayPropagator())); + } + + if (codeSimplificationAdvanced) + { + // Fill out constants into the arrays of synthetic classes, + // notably the arrays of the classes generated for enum switch + // statements. + InvocationUnit loadingInvocationUnit = + new LoadingInvocationUnit(valueFactory, + fieldPropagationValue, + methodPropagationParameter, + methodPropagationReturnvalue); + + programClassPool.classesAccept( + new ClassAccessFilter(ClassConstants.ACC_SYNTHETIC, 0, + new AllMethodVisitor( + new AllAttributeVisitor( + new PartialEvaluator(valueFactory, loadingInvocationUnit, false))))); + } + } + + if (codeSimplificationAdvanced) + { + ParallelAllClassVisitor.ClassVisitorFactory simplifyingCodeVisitor = + new ParallelAllClassVisitor.ClassVisitorFactory() + { + public ClassVisitor createClassVisitor() + { + // Perform partial evaluation again, now loading any previously stored + // values for fields, method parameters, and method return values. + ValueFactory valueFactory = new IdentifiedValueFactory(); + + SimplifiedInvocationUnit loadingInvocationUnit = + new LoadingInvocationUnit(valueFactory, + fieldPropagationValue, + methodPropagationParameter, + methodPropagationReturnvalue); + + return + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Simplifying code", + new OptimizationCodeAttributeFilter( + new EvaluationSimplifier( + new PartialEvaluator(valueFactory, loadingInvocationUnit, false), + codeSimplificationAdvancedCounter))))); + } + }; + + // Simplify based on partial evaluation, propagating constant + // field values, method parameter values, and return values. + programClassPool.accept( + new TimedClassPoolVisitor("Simplifying code", + new ParallelAllClassVisitor( + simplifyingCodeVisitor))); + } + + if (codeRemovalAdvanced) + { + ParallelAllClassVisitor.ClassVisitorFactory shrinkingCodeVisitor = + new ParallelAllClassVisitor.ClassVisitorFactory() + { + public ClassVisitor createClassVisitor() + { + // Perform partial evaluation again, now loading any previously stored + // values for fields, method parameters, and method return values. + ValueFactory valueFactory = new IdentifiedValueFactory(); + + SimplifiedInvocationUnit loadingInvocationUnit = + new LoadingInvocationUnit(valueFactory, + fieldPropagationValue, + methodPropagationParameter, + methodPropagationReturnvalue); + + // Trace the construction of reference values. + ReferenceTracingValueFactory referenceTracingValueFactory = + new ReferenceTracingValueFactory(valueFactory); + + return + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Shrinking code", + new OptimizationCodeAttributeFilter( + new EvaluationShrinker( + new InstructionUsageMarker( + new PartialEvaluator(referenceTracingValueFactory, + new ParameterTracingInvocationUnit(loadingInvocationUnit), + !codeSimplificationAdvanced, + referenceTracingValueFactory), + true), true, deletedCounter, addedCounter))))); + } + }; + + // Remove code based on partial evaluation, also removing unused + // parameters from method invocations, and making methods static + // if possible. + programClassPool.accept( + new TimedClassPoolVisitor("Shrinking code", + new ParallelAllClassVisitor( + shrinkingCodeVisitor))); + } + + if (methodRemovalParameter) + { + // Shrink the parameters in the method descriptors. + programClassPool.classesAccept( + new AllMethodVisitor( + new UnusedParameterMethodFilter( + new OptimizationInfoMemberFilter( + new MethodDescriptorShrinker(methodRemovalParameterCounter1))))); + } + + 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.ACC_STATIC, + new MethodStaticizer(methodMarkingStaticCounter))))); + } + + if (methodRemovalParameterCounter1.getCount() > 0) + { + // 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 (methodRemovalParameterCounter1.getCount() > 0 || + methodMarkingPrivate || + // Methods are only marked private later on. + //methodMarkingPrivateCounter .getCount() > 0 || + methodMarkingStaticCounter .getCount() > 0) + { + // Remove all unused parameters from the corresponding byte code, + // shifting all remaining variables. + // This operation also updates the local variable frame sizes. + programClassPool.classesAccept( + new AllMethodVisitor( + new UnusedParameterMethodFilter( + new AllAttributeVisitor( + new ParameterShrinker(methodRemovalParameterCounter2))))); + + // Remove all unused parameters in the optimization info. + programClassPool.classesAccept( + new AllMethodVisitor( + new UnusedParameterMethodFilter( + new AllAttributeVisitor( + new UnusedParameterOptimizationInfoUpdater())))); + } + else if (codeRemovalAdvanced) + { + // Just update the local variable frame sizes. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new OptimizationCodeAttributeFilter( + new StackSizeUpdater())))); + } + + if (methodRemovalParameter && + methodRemovalParameterCounter2.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()); + } + } + + // 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. + StackSizeComputer stackSizeComputer = new StackSizeComputer(); + + programClassPool.accept( + new TimedClassPoolVisitor("Marking method and referenced class properties", + new MultiClassVisitor( + // Mark classes. + new OptimizationInfoClassFilter( + new MultiClassVisitor( + new PackageVisibleMemberContainingClassMarker(), + new WrapperClassMarker(), + + new AllConstantVisitor( + new PackageVisibleMemberInvokingClassMarker()) + )), + + // Mark methods. + new AllMethodVisitor( + new OptimizationInfoMemberFilter( + new AllAttributeVisitor( + new DebugAttributeVisitor("Marking method properties", + new MultiAttributeVisitor( + stackSizeComputer, + new CatchExceptionMarker(), + + new AllInstructionVisitor( + new MultiInstructionVisitor( + new SuperInvocationMarker(), + new DynamicInvocationMarker(), + new BackwardBranchMarker(), + new AccessMethodMarker(), + new SynchronizedBlockMethodMarker(), + new NonEmptyStackReturnMarker(stackSizeComputer) + )) + ))))), + + // Mark referenced classes and methods. + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Marking referenced class properties", + new MultiAttributeVisitor( + new AllExceptionInfoVisitor( + new ExceptionHandlerConstantVisitor( + new ReferencedClassVisitor( + new OptimizationInfoClassFilter( + new CaughtClassMarker())))), + + new AllInstructionVisitor( + new MultiInstructionVisitor( + new InstantiationClassMarker(), + new InstanceofClassMarker(), + new DotClassMarker(), + new MethodInvocationMarker() + )) + )))) + ))); + + if (classMergingWrapper) + { + // Merge wrapper classes into their wrapped classes. + programClassPool.accept( + new TimedClassPoolVisitor("Merging wrapper classes", + // Exclude injected classes - they might not end up in the output. + new WrapperClassMerger(configuration.allowAccessModification, + classMergingWrapperCounter))); + + if (classMergingWrapperCounter.getCount() > 0) + { + // Fix all uses of wrapper classes. + programClassPool.classesAccept( + new RetargetedClassFilter(null, + new AllMethodVisitor( + new AllAttributeVisitor( + new WrapperClassUseSimplifier())))); + } + } + + if (classMergingVertical) + { + // Merge subclasses up into their superclasses or + // merge interfaces down into their implementing classes. + programClassPool.accept( + new TimedClassPoolVisitor("Merging classes vertically", + // Exclude injected classes - they might not end up in the output. + new VerticalClassMerger(configuration.allowAccessModification, + configuration.mergeInterfacesAggressively, + classMergingVerticalCounter))); + } + + if (classMergingHorizontal) + { + // Merge classes into their sibling classes. + programClassPool.accept( + new TimedClassPoolVisitor("Merging classes horizontally", + // Exclude injected classes - they might not end up in the output. + new ClassNameFilter( + new NotMatcher( + new CollectionMatcher(injectedClassNameMap.getValues())), + new HorizontalClassMerger(configuration.allowAccessModification, + configuration.mergeInterfacesAggressively, + classMergingHorizontalCounter)))); + } + + if (classMergingVerticalCounter .getCount() > 0 || + classMergingHorizontalCounter.getCount() > 0 || + classMergingWrapperCounter .getCount() > 0) + { + // Clean up inner class attributes to avoid loops. + programClassPool.classesAccept(new RetargetedInnerClassAttributeRemover()); + + // Update references to merged classes: first the referenced + // classes, then the various actual descriptors. + // Leave retargeted classes themselves unchanged and valid, + // in case they aren't shrunk later on. + programClassPool.classesAccept(new RetargetedClassFilter(null, new TargetClassChanger())); + programClassPool.classesAccept(new RetargetedClassFilter(null, new ClassReferenceFixer(true))); + programClassPool.classesAccept(new RetargetedClassFilter(null, new MemberReferenceFixer())); + + if (configuration.allowAccessModification) + { + // Fix the access flags of referenced merged classes and their + // class members. + programClassPool.classesAccept(new AccessFixer()); + } + + // Fix the access flags of the inner classes information. + // DGD-63: don't change the access flags of inner classes + // that have not been renamed (Guice). + programClassPool.classesAccept( + new KeptClassFilter(null, + 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.accept( + new TimedClassPoolVisitor("Inlining single methods", + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Inlining single methods", + new OptimizationCodeAttributeFilter( + new MethodInliner(configuration.microEdition, + configuration.android, + configuration.allowAccessModification, + true, + methodInliningUniqueCounter))))))); + } + + if (methodInliningShort) + { + // Inline short methods. + programClassPool.accept( + new TimedClassPoolVisitor("Inlining short methods", + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Inlining short methods", + new OptimizationCodeAttributeFilter( + new MethodInliner(configuration.microEdition, + configuration.android, + configuration.allowAccessModification, + false, + methodInliningShortCounter))))))); + } + + if (methodInliningTailrecursion) + { + // Simplify tail recursion calls. + programClassPool.accept( + new TimedClassPoolVisitor("Simplifying tail recursion", + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Simplifying tail recursion", + new OptimizationCodeAttributeFilter( + 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.ACC_INTERFACE, + new AllFieldVisitor( + new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, + new MemberPrivatizer(fieldMarkingPrivateCounter))))); + } + + if (methodMarkingPrivate) + { + // Make all non-private methods private, whereever possible. + programClassPool.classesAccept( + new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE, + new AllMethodVisitor( + new MemberAccessFilter(0, ClassConstants.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 AccessFixer()); + } + + if (methodRemovalParameterCounter2.getCount() > 0 || + classMergingVerticalCounter .getCount() > 0 || + classMergingHorizontalCounter .getCount() > 0 || + classMergingWrapperCounter .getCount() > 0 || + methodMarkingPrivateCounter .getCount() > 0 || + ((methodInliningUniqueCounter .getCount() > 0 || + methodInliningShortCounter .getCount() > 0 || + methodInliningTailrecursionCounter.getCount() > 0) && + configuration.allowAccessModification)) + { + // Fix invocations of interface methods, or 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.accept( + new TimedClassPoolVisitor("Sharing common code", + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Sharing common code", + new OptimizationCodeAttributeFilter( + new GotoCommonCodeReplacer(codeMergingCounter))))))); + } + + if (codeSimplificationPeephole) + { + ParallelAllClassVisitor.ClassVisitorFactory peepHoleOptimizer = + new ParallelAllClassVisitor.ClassVisitorFactory() + { + public ClassVisitor createClassVisitor() + { + // 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(); + + InstructionSequenceConstants sequences = + new InstructionSequenceConstants(programClassPool, + libraryClassPool); + + List peepholeOptimizations = createPeepholeOptimizations(sequences, + branchTargetFinder, + codeAttributeEditor, + codeSimplificationVariableCounter, + codeSimplificationArithmeticCounter, + codeSimplificationCastCounter, + codeSimplificationFieldCounter, + codeSimplificationBranchCounter, + codeSimplificationObjectCounter, + codeSimplificationStringCounter, + codeSimplificationMathCounter, + codeSimplificationAndroidMathCounter); + + // Convert the list into an array. + InstructionVisitor[] peepholeOptimizationsArray = + new InstructionVisitor[peepholeOptimizations.size()]; + peepholeOptimizations.toArray(peepholeOptimizationsArray); + + return + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Peephole optimizations", + new OptimizationCodeAttributeFilter( + new PeepholeOptimizer(branchTargetFinder, codeAttributeEditor, + new MultiInstructionVisitor( + peepholeOptimizationsArray)))))); + } + }; + + // Perform the peephole optimisations. + programClassPool.accept( + new TimedClassPoolVisitor("Peephole optimizations", + new ParallelAllClassVisitor( + peepHoleOptimizer))); + } + + if (codeRemovalException) + { + // Remove unnecessary exception handlers. + programClassPool.accept( + new TimedClassPoolVisitor("Unreachable exception removal", + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Unreachable exception removal", + new OptimizationCodeAttributeFilter( + new UnreachableExceptionRemover(codeRemovalExceptionCounter))))))); + } + + if (codeRemovalSimple) + { + // Remove unreachable code. + programClassPool.accept( + new TimedClassPoolVisitor("Unreachable code removal", + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Unreachable code removal", + new OptimizationCodeAttributeFilter( + new UnreachableCodeRemover(deletedCounter))))))); + } + + if (codeRemovalVariable) + { + // Remove all unused local variables. + programClassPool.accept( + new TimedClassPoolVisitor("Variable shrinking", + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Variable shrinking", + new OptimizationCodeAttributeFilter( + new VariableShrinker(codeRemovalVariableCounter))))))); + } + + if (codeAllocationVariable) + { + ParallelAllClassVisitor.ClassVisitorFactory optimizingVariablesVisitor = + new ParallelAllClassVisitor.ClassVisitorFactory() + { + public ClassVisitor createClassVisitor() + { + return + new AllMethodVisitor( + new AllAttributeVisitor( + new DebugAttributeVisitor("Variable optimizations", + new OptimizationCodeAttributeFilter( + new VariableOptimizer(false, codeAllocationVariableCounter))))); + } + }; + + // Optimize the variables. + programClassPool.accept( + new TimedClassPoolVisitor("Variable optimizations", + new ParallelAllClassVisitor( + optimizingVariablesVisitor))); + } + + // Remove unused constants. + programClassPool.accept( + new TimedClassPoolVisitor("Shrinking constant pool", + new ConstantPoolShrinker())); + + int classMarkingFinalCount = classMarkingFinalCounter .getCount(); + int classUnboxingEnumCount = classUnboxingEnumCounter .getCount(); + int classMergingVerticalCount = classMergingVerticalCounter .getCount(); + int classMergingHorizontalCount = classMergingHorizontalCounter .getCount(); + int classMergingWrapperCount = classMergingWrapperCounter .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 methodMarkingSynchronizedCount = methodMarkingSynchronizedCounter .getCount(); + int methodRemovalParameterCount1 = methodRemovalParameterCounter1 .getCount() - initializerFixCounter1.getCount() - initializerFixCounter2.getCount(); + int methodRemovalParameterCount2 = methodRemovalParameterCounter2 .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 codeSimplificationObjectCount = codeSimplificationObjectCounter .getCount(); + int codeSimplificationStringCount = codeSimplificationStringCounter .getCount(); + int codeSimplificationMathCount = codeSimplificationMathCounter .getCount(); + int codeSimplificationAndroidMathCount = codeSimplificationAndroidMathCounter .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 unboxed enum classes: " + classUnboxingEnumCount + disabled(classUnboxingEnum)); + 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 merged wrapper classes: " + classMergingWrapperCount + disabled(classMergingWrapper)); + 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 desynchronized methods: " + methodMarkingSynchronizedCount + disabled(methodMarkingSynchronized)); + System.out.println(" Number of simplified method signatures: " + methodRemovalParameterCount1 + disabled(methodRemovalParameter)); + System.out.println(" Number of removed method parameters: " + methodRemovalParameterCount2 + 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 object peephole optimizations: " + codeSimplificationObjectCount + disabled(codeSimplificationObject)); + System.out.println(" Number of string peephole optimizations: " + codeSimplificationStringCount + disabled(codeSimplificationString)); + System.out.println(" Number of math peephole optimizations: " + codeSimplificationMathCount + disabled(codeSimplificationMath)); + if (configuration.android) + System.out.println(" Number of Android math peephole optimizations: " + codeSimplificationAndroidMathCount + disabled(codeSimplificationMath)); + 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 || + classUnboxingEnumCount > 0 || + classMergingVerticalCount > 0 || + classMergingHorizontalCount > 0 || + classMergingWrapperCount > 0 || + fieldRemovalWriteonlyCount > 0 || // TODO: The write-only field counter may be optimistic about removal. + fieldMarkingPrivateCount > 0 || + methodMarkingPrivateCount > 0 || + methodMarkingStaticCount > 0 || + methodMarkingFinalCount > 0 || + fieldPropagationValueCount > 0 || + methodRemovalParameterCount1 > 0 || + methodRemovalParameterCount2 > 0 || + methodPropagationParameterCount > 0 || + methodPropagationReturnvalueCount > 0 || + methodInliningShortCount > 0 || + methodInliningUniqueCount > 0 || + methodInliningTailrecursionCount > 0 || + codeMergingCount > 0 || + codeSimplificationVariableCount > 0 || + codeSimplificationArithmeticCount > 0 || + codeSimplificationCastCount > 0 || + codeSimplificationFieldCount > 0 || + codeSimplificationBranchCount > 0 || + codeSimplificationObjectCount > 0 || + codeSimplificationStringCount > 0 || + codeSimplificationMathCount > 0 || + codeSimplificationAndroidMathCount > 0 || + codeSimplificationAdvancedCount > 0 || + codeRemovalCount > 0 || + codeRemovalVariableCount > 0 || + codeRemovalExceptionCount > 0 || + codeAllocationVariableCount > 0; + } + + + private List createPeepholeOptimizations(InstructionSequenceConstants sequences, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor, + InstructionCounter codeSimplificationVariableCounter, + InstructionCounter codeSimplificationArithmeticCounter, + InstructionCounter codeSimplificationCastCounter, + InstructionCounter codeSimplificationFieldCounter, + InstructionCounter codeSimplificationBranchCounter, + InstructionCounter codeSimplificationObjectCounter, + InstructionCounter codeSimplificationStringCounter, + InstructionCounter codeSimplificationMathCounter, + InstructionCounter codeSimplificationAndroidMathCounter) + { + List peepholeOptimizations = new ArrayList(); + + if (codeSimplificationVariable) + { + // Peephole optimizations involving local variables. + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.VARIABLE_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationVariableCounter)); + } + + if (codeSimplificationArithmetic) + { + // Peephole optimizations involving arithmetic operations. + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.ARITHMETIC_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationArithmeticCounter)); + } + + if (codeSimplificationCast) + { + // Peephole optimizations involving cast operations. + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.CAST_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationCastCounter)); + } + + if (codeSimplificationField) + { + // Peephole optimizations involving fields. + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.FIELD_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationFieldCounter)); + } + + if (codeSimplificationBranch) + { + // Peephole optimizations involving branches. + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.BRANCH_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationBranchCounter)); + + // Include optimization of branches to branches and returns. + peepholeOptimizations.add( + new GotoGotoReplacer(codeAttributeEditor, codeSimplificationBranchCounter)); + peepholeOptimizations.add( + new GotoReturnReplacer(codeAttributeEditor, codeSimplificationBranchCounter)); + } + + if (codeSimplificationObject) + { + // Peephole optimizations involving branches. + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.OBJECT_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationObjectCounter)); + } + + if (codeSimplificationString) + { + // Peephole optimizations involving branches. + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.STRING_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationStringCounter)); + } + + if (codeSimplificationMath) + { + // Peephole optimizations involving math. + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.MATH_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationMathCounter)); + + if (configuration.android) + { + peepholeOptimizations.add( + new InstructionSequencesReplacer(sequences.CONSTANTS, + sequences.MATH_ANDROID_SEQUENCES, + branchTargetFinder, codeAttributeEditor, codeSimplificationAndroidMathCounter)); + } + } + + return peepholeOptimizations; + } + + + /** + * 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)"; + } + + + /** + * A simple class pool visitor that will output timing information. + */ + private class TimedClassPoolVisitor + implements ClassPoolVisitor + { + private final String message; + private final ClassPoolVisitor classPoolVisitor; + + public TimedClassPoolVisitor(String message, ClassVisitor classVisitor) + { + this(message, new AllClassVisitor(classVisitor)); + } + + public TimedClassPoolVisitor(String message, ClassPoolVisitor classPoolVisitor) + { + this.message = message; + this.classPoolVisitor = classPoolVisitor; + } + + + // Implementations for ClassPoolVisitor. + + public void visitClassPool(ClassPool classPool) + { + long start = 0; + + if (DETAILS) + { + System.out.print(message); + System.out.print(getPadding(message.length(), 48)); + start = System.currentTimeMillis(); + } + + classPool.accept(classPoolVisitor); + + if (DETAILS) + { + long end = System.currentTimeMillis(); + System.out.println(String.format(" took: %6d ms", (end - start))); + } + } + + + // Private helper methods + + private String getPadding(int pos, int size) + { + StringBuilder sb = new StringBuilder(); + for (int i = pos; i < size; i++) + { + sb.append('.'); + } + return sb.toString(); + } + } +} diff --git a/src/proguard/optimize/ParameterShrinker.java b/core/src/proguard/optimize/ParameterShrinker.java similarity index 88% rename from src/proguard/optimize/ParameterShrinker.java rename to core/src/proguard/optimize/ParameterShrinker.java index 8d6ff621f..0f540294b 100644 --- a/src/proguard/optimize/ParameterShrinker.java +++ b/core/src/proguard/optimize/ParameterShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,8 +25,8 @@ 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; +import proguard.classfile.visitor.*; +import proguard.optimize.info.*; /** * This AttributeVisitor removes unused parameters from the code of the methods @@ -48,7 +48,7 @@ public class ParameterShrinker //*/ - private final MemberVisitor extraVariableMemberVisitor; + private final MemberVisitor extraUnusedParameterMethodVisitor; private final VariableRemapper variableRemapper = new VariableRemapper(); @@ -64,12 +64,12 @@ public ParameterShrinker() /** * Creates a new ParameterShrinker with an extra visitor. - * @param extraVariableMemberVisitor an optional extra visitor for all - * removed parameters. + * @param extraUnusedParameterMethodVisitor an optional extra visitor for + * all removed parameters. */ - public ParameterShrinker(MemberVisitor extraVariableMemberVisitor) + public ParameterShrinker(MemberVisitor extraUnusedParameterMethodVisitor) { - this.extraVariableMemberVisitor = extraVariableMemberVisitor; + this.extraUnusedParameterMethodVisitor = extraUnusedParameterMethodVisitor; } @@ -127,9 +127,9 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt variableMap[parameterIndex] = unusedParameterIndex++; // Visit the method, if required. - if (extraVariableMemberVisitor != null) + if (extraUnusedParameterMethodVisitor != null) { - method.accept(clazz, extraVariableMemberVisitor); + method.accept(clazz, extraUnusedParameterMethodVisitor); } } } diff --git a/src/proguard/optimize/TailRecursionSimplifier.java b/core/src/proguard/optimize/TailRecursionSimplifier.java similarity index 85% rename from src/proguard/optimize/TailRecursionSimplifier.java rename to core/src/proguard/optimize/TailRecursionSimplifier.java index 3e664b3df..c92c1ec37 100644 --- a/src/proguard/optimize/TailRecursionSimplifier.java +++ b/core/src/proguard/optimize/TailRecursionSimplifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -55,6 +55,7 @@ public class TailRecursionSimplifier private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); private final MyRecursionChecker recursionChecker = new MyRecursionChecker(); + private final StackSizeComputer stackSizeComputer = new StackSizeComputer(); private Method targetMethod; private boolean inlinedAny; @@ -118,12 +119,12 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt // 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); + // Copy the exceptions. + codeAttribute.exceptionsAccept(clazz, method, this); + codeAttributeComposer.endCodeFragment(); codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); @@ -176,35 +177,40 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c if (recursionChecker.isRecursive()) { - if (DEBUG) + // Is the stack empty after the return? + stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute); + + if (stackSizeComputer.getStackSizeAfter(nextOffset) == 0) { - System.out.println("TailRecursionSimplifier: ["+ - clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"], inlining "+constantInstruction.toString(offset)); - } + if (DEBUG) + { + System.out.println("TailRecursionSimplifier: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"], inlining "+constantInstruction.toString(offset)); + } - // Append a label. - codeAttributeComposer.appendLabel(offset); + // Append a label. + codeAttributeComposer.appendLabel(offset); - storeParameters(clazz, method); + storeParameters(clazz, method); - // Branch back to the start of the method. - int gotoOffset = offset + 1; - codeAttributeComposer.appendInstruction(gotoOffset, - new BranchInstruction(InstructionConstants.OP_GOTO, -gotoOffset)); + // 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. + // The original return instruction will be + // removed elsewhere, if possible. - // Remember that the code has changed. - inlinedAny = true; + // Remember that the code has changed. + inlinedAny = true; - if (extraTailRecursionVisitor != null) - { - extraTailRecursionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); - } + if (extraTailRecursionVisitor != null) + { + extraTailRecursionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } - // The invocation itself is no longer necessary. - return; + // The invocation itself is no longer necessary. + return; + } } } } diff --git a/src/proguard/optimize/WriteOnlyFieldFilter.java b/core/src/proguard/optimize/WriteOnlyFieldFilter.java similarity index 97% rename from src/proguard/optimize/WriteOnlyFieldFilter.java rename to core/src/proguard/optimize/WriteOnlyFieldFilter.java index b6c00b9d8..fc36a15cc 100644 --- a/src/proguard/optimize/WriteOnlyFieldFilter.java +++ b/core/src/proguard/optimize/WriteOnlyFieldFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/optimize/evaluation/EvaluationShrinker.java b/core/src/proguard/optimize/evaluation/EvaluationShrinker.java new file mode 100644 index 000000000..15591ce00 --- /dev/null +++ b/core/src/proguard/optimize/evaluation/EvaluationShrinker.java @@ -0,0 +1,1548 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.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.SimplifiedVisitor; +import proguard.classfile.visitor.*; +import proguard.evaluation.TracedStack; +import proguard.evaluation.value.*; +import proguard.optimize.info.ParameterUsageMarker; + +/** + * This AttributeVisitor shrinks the code attributes that it visits, based + * on partial evaluation. + * + * @author Eric Lafortune + */ +public class EvaluationShrinker +extends SimplifiedVisitor +implements AttributeVisitor, + ExceptionInfoVisitor +{ + //* + private static final boolean DEBUG = false; + private static final boolean DEBUG_RESULTS = false; + /*/ + private static boolean DEBUG = System.getProperty("es") != null; + private static boolean DEBUG_RESULTS = DEBUG; + //*/ + + 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 InstructionUsageMarker instructionUsageMarker; + private final boolean runInstructionUsageMarker; + private final InstructionVisitor extraDeletedInstructionVisitor; + private final InstructionVisitor extraAddedInstructionVisitor; + + private final MyStaticInvocationFixer staticInvocationFixer = new MyStaticInvocationFixer(); + private final MyBackwardBranchFixer backwardBranchFixer = new MyBackwardBranchFixer(); + private final MyNonReturningSubroutineFixer nonReturningSubroutineFixer = new MyNonReturningSubroutineFixer(); + private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer(); + private final MyInstructionDeleter instructionDeleter = new MyInstructionDeleter(); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true); + + + /** + * Creates a new EvaluationShrinker. + */ + public EvaluationShrinker() + { + this(new PartialEvaluator(), true, null, null); + } + + + /** + * Creates a new EvaluationShrinker. + * @param partialEvaluator the partial evaluator that will + * analyze the code. + * @param runPartialEvaluator specifies whether the partial + * evaluator should be run for each + * method, or if some other class is + * already doing this. + * @param extraDeletedInstructionVisitor an optional extra visitor for all + * deleted instructions. + * @param extraAddedInstructionVisitor an optional extra visitor for all + * added instructions. + */ + public EvaluationShrinker(PartialEvaluator partialEvaluator, + boolean runPartialEvaluator, + InstructionVisitor extraDeletedInstructionVisitor, + InstructionVisitor extraAddedInstructionVisitor) + { + this(new InstructionUsageMarker(partialEvaluator, runPartialEvaluator), + true, + extraDeletedInstructionVisitor, + extraAddedInstructionVisitor); + } + + + /** + * Creates a new EvaluationShrinker. + * @param instructionUsageMarker the instruction usage marker that + * will analyze the code. + * @param runInstructionUsageMarker specifies whether the usage + * marker should be run for each + * method, or if some other class is + * already doing this. + * @param extraDeletedInstructionVisitor an optional extra visitor for all + * deleted instructions. + * @param extraAddedInstructionVisitor an optional extra visitor for all + * added instructions. + */ + public EvaluationShrinker(InstructionUsageMarker instructionUsageMarker, + boolean runInstructionUsageMarker, + InstructionVisitor extraDeletedInstructionVisitor, + InstructionVisitor extraAddedInstructionVisitor) + { + this.instructionUsageMarker = instructionUsageMarker; + this.runInstructionUsageMarker = runInstructionUsageMarker; + 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()+")"); + + ex.printStackTrace(); + 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("EvaluationShrinker ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + } + + // Analyze the method. + if (runInstructionUsageMarker) + { + instructionUsageMarker.visitCodeAttribute(clazz, method, codeAttribute); + } + + int codeLength = codeAttribute.u4codeLength; + + if (DEBUG) System.out.println(); + + + // Reset the code changes. + codeAttributeEditor.reset(codeLength); + + // Replace virtual invocations by static invocations, where neceesary. + if (DEBUG) System.out.println("Static invocation fixing:"); + + codeAttribute.instructionsAccept(clazz, method, + instructionUsageMarker.necessaryInstructionFilter(true, + staticInvocationFixer)); + + if (DEBUG) System.out.println(); + + + // Replace traced but unnecessary 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("Backward branch fixing:"); + + codeAttribute.instructionsAccept(clazz, method, + instructionUsageMarker.tracedInstructionFilter(true, + instructionUsageMarker.necessaryInstructionFilter(false, + backwardBranchFixer))); + + 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:"); + + codeAttribute.instructionsAccept(clazz, method, + instructionUsageMarker.necessaryInstructionFilter(true, + nonReturningSubroutineFixer)); + + if (DEBUG) System.out.println(); + + + // Locally fix instructions, in order to keep the stack consistent. + if (DEBUG) System.out.println("Stack consistency fixing:"); + + codeAttribute.instructionsAccept(clazz, method, + instructionUsageMarker.tracedInstructionFilter(true, + stackConsistencyFixer)); + + if (DEBUG) System.out.println(); + + + // Delete all instructions that are not used. + if (DEBUG) System.out.println("Deleting unused instructions"); + + codeAttribute.instructionsAccept(clazz, method, + instructionUsageMarker.necessaryInstructionFilter(false, + instructionDeleter)); + + if (DEBUG) System.out.println(); + + + if (DEBUG_RESULTS) + { + System.out.println("Simplification results:"); + + int offset = 0; + do + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + System.out.println((instructionUsageMarker.isInstructionNecessary(offset) ? " + " : + instructionUsageMarker.isExtraPushPopInstructionNecessary(offset) ? " ~ " : + " - ") + + instruction.toString(offset)); + + if (instructionUsageMarker.isTraced(offset)) + { + InstructionOffsetValue branchTargets = instructionUsageMarker.branchTargets(offset); + if (branchTargets != null) + { + System.out.println(" has overall been branching to "+branchTargets); + } + + boolean deleted = codeAttributeEditor.deleted[offset]; + if (instructionUsageMarker.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); + } + + // Clear exception handlers that are not necessary. + codeAttribute.exceptionsAccept(clazz, method, this); + + // Apply all accumulated changes to the code. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + + + /** + * This MemberVisitor converts virtual method invocations into static + * method invocations if the 'this' parameter isn't used. + */ + private class MyStaticInvocationFixer + 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_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) + { + // Make the method invocation static, if possible. + if ((programMethod.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 && + !ParameterUsageMarker.isParameterUsed(programMethod, 0)) + { + replaceByStaticInvocation(programClass, + invocationOffset, + invocationInstruction); + } + } + } + + + /** + * This InstructionVisitor replaces all backward branches by + * infinite loops. + */ + private class MyBackwardBranchFixer + extends SimplifiedVisitor + implements InstructionVisitor + { + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Is it a traced but unmarked backward branch, without an unmarked + // straddling forward branch? Note that this is still a heuristic. + if (isAllSmallerThanOrEqual(instructionUsageMarker.branchTargets(offset), + offset) && + !isAnyUnnecessaryInstructionBranchingOver(lastNecessaryInstructionOffset(offset), + offset)) + { + replaceByInfiniteLoop(clazz, offset); + + if (DEBUG) System.out.println(" Setting infinite loop instead of "+instruction.toString(offset)); + } + } + + + /** + * 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 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 (instructionUsageMarker.isInstructionNecessary(instructionOffset)) + { + return offset; + } + } + + return 0; + } + } + + + /** + * This InstructionVisitor appends infinite loops after all visited + * non-returning subroutine invocations. + */ + private class MyNonReturningSubroutineFixer + 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) + { + // Is it a necessary subroutine invocation? + if (branchInstruction.canonicalOpcode() == InstructionConstants.OP_JSR) + { + int nextOffset = offset + branchInstruction.length(offset); + if (!instructionUsageMarker.isInstructionNecessary(nextOffset)) + { + replaceByInfiniteLoop(clazz, nextOffset); + + if (DEBUG) System.out.println(" Adding infinite loop at ["+nextOffset+"] after "+branchInstruction.toString(offset)); + } + } + } + } + + + /** + * 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 (instructionUsageMarker.isInstructionNecessary(offset)) + { + // Check all stack entries that are popped. + // Unusual case: an exception handler with an exception that is + // no longer consumed directly by a method. + // Typical case: a freshly marked variable initialization that + // requires some value on the stack. + int popCount = instruction.stackPopCount(clazz); + if (popCount > 0) + { + TracedStack tracedStack = + instructionUsageMarker.getStackBefore(offset); + + int stackSize = tracedStack.size(); + + int requiredPopCount = 0; + int requiredPushCount = 0; + for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) + { + boolean stackEntryUnwantedBefore = + instructionUsageMarker.isStackEntryUnwantedBefore( offset, stackIndex); + boolean stackEntryPresentBefore = + instructionUsageMarker.isStackEntryPresentBefore( offset, stackIndex); + + if (stackEntryUnwantedBefore) + { + if (stackEntryPresentBefore) + { + // Remember to pop it. + requiredPopCount++; + } + } + else + { + if (!stackEntryPresentBefore) + { + // Remember to push some value. + requiredPushCount++; + } + } + } + + // Pop some unnecessary stack entries. + if (requiredPopCount > 0) + { + if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset)); + + insertPopInstructions(offset, false, true, popCount); + } + + // Push some necessary stack entries. + if (requiredPushCount > 0) + { + Value value = tracedStack.getTop(0); + + if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset)); + + if (requiredPushCount > (value.isCategory2() ? 2 : 1)) + { + throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"] at ["+offset+"]"); + } + + insertPushInstructions(offset, false, true, value.computationalType()); + } + } + + // 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 = + instructionUsageMarker.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 (!instructionUsageMarker.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, false, requiredPopCount); + } + } + } + else if (instructionUsageMarker.isExtraPushPopInstructionNecessary(offset)) + { + // 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 = + instructionUsageMarker.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 (instructionUsageMarker.isStackEntryPresentBefore(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, false, expectedPopCount); + } + } + + // Check all stack entries that would be pushed. + // Typical case: a corresponding stack entry is pushed + // elsewhere so it still has to be pushed here. + int pushCount = instruction.stackPushCount(clazz); + if (pushCount > 0) + { + TracedStack tracedStack = + instructionUsageMarker.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 (instructionUsageMarker.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, false, tracedStack.getTop(0).computationalType()); + } + } + } + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + if (instructionUsageMarker.isInstructionNecessary(offset) && + isDupOrSwap(simpleInstruction)) + { + int topBefore = instructionUsageMarker.getStackBefore(offset).size() - 1; + int topAfter = instructionUsageMarker.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 suitable (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); + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + if (instructionUsageMarker.isInstructionNecessary(offset)) + { + if (branchInstruction.stackPopCount(clazz) > 0 && + !instructionUsageMarker.isStackEntryPresentBefore(offset, instructionUsageMarker.getStackBefore(offset).size() - 1)) + { + // Replace the branch instruction by a simple goto. + Instruction replacementInstruction = new BranchInstruction(InstructionConstants.OP_GOTO, + branchInstruction.branchOffset); + codeAttributeEditor.replaceInstruction(offset, + replacementInstruction); + + if (DEBUG) System.out.println(" Replacing branch instruction "+branchInstruction.toString(offset)+" by "+replacementInstruction.toString()); + } + } + else + { + visitAnyInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + if (instructionUsageMarker.isInstructionNecessary(offset)) + { + if (switchInstruction.stackPopCount(clazz) > 0 && + !instructionUsageMarker.isStackEntryPresentBefore(offset, instructionUsageMarker.getStackBefore(offset).size() - 1)) + { + // Replace the switch instruction by a simple goto. + Instruction replacementInstruction = new BranchInstruction(InstructionConstants.OP_GOTO, + switchInstruction.defaultOffset); + codeAttributeEditor.replaceInstruction(offset, + replacementInstruction); + + if (DEBUG) System.out.println(" Replacing switch instruction "+switchInstruction.toString(offset)+" by "+replacementInstruction.toString()); + } + } + else + { + visitAnyInstruction(clazz, method, codeAttribute, offset, switchInstruction); + } + } + + + /** + * 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 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 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore); + + boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter); + boolean stackEntryNecessary1 = instructionUsageMarker.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 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore); + boolean stackEntryPresent1 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore - 1); + + boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter); + boolean stackEntryNecessary1 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = instructionUsageMarker.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 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore); + boolean stackEntryPresent1 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore - 1); + boolean stackEntryPresent2 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore - 2); + + boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter); + boolean stackEntryNecessary1 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = instructionUsageMarker.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 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore); + boolean stackEntryPresent1 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, + topBefore - + 1); + + boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter); + boolean stackEntryNecessary1 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = instructionUsageMarker.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 = instructionUsageMarker.isStackEntriesPresentBefore(instructionOffset, topBefore, topBefore - 1); + boolean stackEntryPresent2 = instructionUsageMarker.isStackEntryPresentBefore( instructionOffset, topBefore - 2); + + boolean stackEntriesNecessary01 = instructionUsageMarker.isStackEntriesNecessaryAfter(instructionOffset, topAfter, topAfter - 1); + boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter( instructionOffset, topAfter - 2); + boolean stackEntriesNecessary34 = instructionUsageMarker.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 = instructionUsageMarker.isStackEntriesPresentBefore(instructionOffset, topBefore, topBefore - 1); + boolean stackEntryPresent2 = instructionUsageMarker.isStackEntryPresentBefore( instructionOffset, topBefore - 2); + boolean stackEntryPresent3 = instructionUsageMarker.isStackEntryPresentBefore( instructionOffset, topBefore - 3); + + boolean stackEntriesNecessary01 = instructionUsageMarker.isStackEntriesNecessaryAfter(instructionOffset, topAfter, topAfter - 1); + boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter( instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = instructionUsageMarker.isStackEntryNecessaryAfter( instructionOffset, topAfter - 3); + boolean stackEntriesNecessary45 = instructionUsageMarker.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 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore); + boolean stackEntryPresent1 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore - 1); + + boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter); + boolean stackEntryNecessary1 = instructionUsageMarker.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 + } + } + + + /** + * This InstructionVisitor deletes all visited instructions. + */ + private class MyInstructionDeleter + extends SimplifiedVisitor + implements InstructionVisitor + { + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + codeAttributeEditor.deleteInstruction(offset); + + // We're allowing edits on deleted instructions. + //codeAttributeEditor.insertBeforeInstruction(offset, (Instruction)null); + //codeAttributeEditor.replaceInstruction(offset, (Instruction)null); + //codeAttributeEditor.insertAfterInstruction(offset, (Instruction)null); + + // Visit the instruction, if required. + if (extraDeletedInstructionVisitor != null) + { + instruction.accept(clazz, method, codeAttribute, offset, extraDeletedInstructionVisitor); + } + } + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + // Is the catch handler necessary? + if (!instructionUsageMarker.isTraced(exceptionInfo.u2handlerPC)) + { + // Make the code block empty, so the code editor can remove it. + exceptionInfo.u2endPC = exceptionInfo.u2startPC; + } + } + + + // Small utility methods. + + /** + * 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 (instructionUsageMarker.isTraced(offset) && + !instructionUsageMarker.isInstructionNecessary(offset) && + isAnyLargerThan(instructionUsageMarker.branchTargets(offset), + instructionOffset2)) + { + 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; + } + + + /** + * 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, + boolean before, + int computationalType) + { + // We can edit an instruction without marking it. + //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. + insertInstruction(offset, replace, before, replacementInstruction); + } + + + /** + * 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, + boolean before, + int popCount) + { + // We can edit an instruction without marking it. + //markInstruction(offset); + + switch (popCount) + { + case 1: + { + // Replace or insert a single pop instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP); + + insertInstruction(offset, replace, before, popInstruction); + break; + } + case 2: + { + // Replace or insert a single pop2 instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP2); + + insertInstruction(offset, replace, before, popInstruction); + 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; + } + + insertInstructions(offset, + replace, + before, + popInstruction, + popInstructions); + break; + } + } + } + + + /** + * Inserts or replaces the given instruction at the given offset. + */ + private void insertInstruction(int offset, + boolean replace, + boolean before, + Instruction instruction) + { + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, instruction); + + if (extraAddedInstructionVisitor != null && + !instructionUsageMarker.isInstructionNecessary(offset)) + { + instruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + else + { + if (before) + { + codeAttributeEditor.insertBeforeInstruction(offset, instruction); + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, instruction); + } + + if (extraAddedInstructionVisitor != null) + { + instruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + + + /** + * Inserts or replaces the given instruction at the given offset. + */ + private void insertInstructions(int offset, + boolean replace, + boolean before, + Instruction instruction, + Instruction[] instructions) + { + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, instructions); + + if (extraAddedInstructionVisitor != null) + { + if (!instructionUsageMarker.isInstructionNecessary(offset)) + { + instruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + + for (int index = 1; index < instructions.length; index++) + { + instructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + else + { + if (before) + { + codeAttributeEditor.insertBeforeInstruction(offset, instructions); + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, instructions); + } + + for (int index = 0; index < instructions.length; index++) + { + if (extraAddedInstructionVisitor != null) + { + instructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + } + + + /** + * 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+"]"); + + // We can edit an instruction without marking it. + //markInstruction(offset); + + // Replace the instruction by an infinite loop. + Instruction replacementInstruction = + new BranchInstruction(InstructionConstants.OP_GOTO, 0); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/core/src/proguard/optimize/evaluation/EvaluationSimplifier.java similarity index 80% rename from src/proguard/optimize/evaluation/EvaluationSimplifier.java rename to core/src/proguard/optimize/evaluation/EvaluationSimplifier.java index 2beb43bbc..14ca598b4 100644 --- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java +++ b/core/src/proguard/optimize/evaluation/EvaluationSimplifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,7 +28,7 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; import proguard.classfile.visitor.ClassPrinter; -import proguard.evaluation.TracedVariables; +import proguard.evaluation.*; import proguard.evaluation.value.*; import proguard.optimize.info.SideEffectInstructionChecker; @@ -51,14 +51,14 @@ public class EvaluationSimplifier //* private static final boolean DEBUG = false; /*/ - private static boolean DEBUG = System.getProperty("es") != null; + private static boolean DEBUG = System.getProperty("es") != null; //*/ private final InstructionVisitor extraInstructionVisitor; private final PartialEvaluator partialEvaluator; private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true); /** @@ -78,7 +78,7 @@ public EvaluationSimplifier() * @param extraInstructionVisitor an optional extra visitor for all * simplified instructions. */ - public EvaluationSimplifier(PartialEvaluator partialEvaluator, + public EvaluationSimplifier(PartialEvaluator partialEvaluator, InstructionVisitor extraInstructionVisitor) { this.partialEvaluator = partialEvaluator; @@ -88,6 +88,7 @@ public EvaluationSimplifier(PartialEvaluator partialEvaluator, // Implementations for AttributeVisitor. + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} @@ -161,15 +162,102 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod { switch (simpleInstruction.opcode) { + case InstructionConstants.OP_IDIV: + case InstructionConstants.OP_IREM: + if (!sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + simpleInstruction)) + { + replaceIntegerPushInstruction(clazz, offset, simpleInstruction); + } + else if (isDivisionByZero(offset, Value.TYPE_INTEGER)) + { + // In case we detected a certain division by zero, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/ArithmeticException"); + } + break; + + case InstructionConstants.OP_LDIV: + case InstructionConstants.OP_LREM: + if (!sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + simpleInstruction)) + { + replaceLongPushInstruction(clazz, offset, simpleInstruction); + } + else if (isDivisionByZero(offset, Value.TYPE_LONG)) + { + // In case we detected a certain division by zero, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/ArithmeticException"); + } + break; + + case InstructionConstants.OP_FDIV: + case InstructionConstants.OP_FREM: + if (!sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + simpleInstruction)) + { + replaceFloatPushInstruction(clazz, offset, simpleInstruction); + } + else if (isDivisionByZero(offset, Value.TYPE_FLOAT)) + { + // In case we detected a certain division by zero, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/ArithmeticException"); + } + break; + + case InstructionConstants.OP_DDIV: + case InstructionConstants.OP_DREM: + if (!sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + simpleInstruction)) + { + replaceDoublePushInstruction(clazz, offset, simpleInstruction); + } + else if (isDivisionByZero(offset, Value.TYPE_DOUBLE)) + { + // In case we detected a certain division by zero, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/ArithmeticException"); + } + break; + case InstructionConstants.OP_IALOAD: case InstructionConstants.OP_BALOAD: case InstructionConstants.OP_CALOAD: case InstructionConstants.OP_SALOAD: + case InstructionConstants.OP_ARRAYLENGTH: + if (!sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + simpleInstruction)) + { + replaceIntegerPushInstruction(clazz, offset, simpleInstruction); + } + else if (isNullReference(offset, simpleInstruction.stackPopCount(clazz) - 1)) + { + // In case we detected a certain access to a null array, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/NullPointerException"); + } + break; + 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: @@ -183,7 +271,6 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod case InstructionConstants.OP_I2B: case InstructionConstants.OP_I2C: case InstructionConstants.OP_I2S: - case InstructionConstants.OP_ARRAYLENGTH: if (!sideEffectInstructionChecker.hasSideEffects(clazz, method, codeAttribute, @@ -195,11 +282,25 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod break; case InstructionConstants.OP_LALOAD: + if (!sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + simpleInstruction)) + { + replaceLongPushInstruction(clazz, offset, simpleInstruction); + } + else if (isNullReference(offset, simpleInstruction.stackPopCount(clazz) - 1)) + { + // In case we detected a certain access to a null array, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/NullPointerException"); + } + break; + 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: @@ -221,11 +322,25 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod break; case InstructionConstants.OP_FALOAD: + if (!sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + simpleInstruction)) + { + replaceFloatPushInstruction(clazz, offset, simpleInstruction); + } + else if (isNullReference(offset, simpleInstruction.stackPopCount(clazz) - 1)) + { + // In case we detected a certain access to a null array, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/NullPointerException"); + } + break; + 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: @@ -241,11 +356,25 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod break; case InstructionConstants.OP_DALOAD: + if (!sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + simpleInstruction)) + { + replaceDoublePushInstruction(clazz, offset, simpleInstruction); + } + else if (isNullReference(offset, simpleInstruction.stackPopCount(clazz) - 1)) + { + // In case we detected a certain access to a null array, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/NullPointerException"); + } + break; + 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: @@ -269,6 +398,29 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod { replaceReferencePushInstruction(clazz, offset, simpleInstruction); } + else if (isNullReference(offset, simpleInstruction.stackPopCount(clazz) - 1)) + { + // In case we detected a certain access to a null array, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/NullPointerException"); + } + break; + + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_AASTORE: + if (SideEffectInstructionChecker.OPTIMIZE_CONSERVATIVELY && + isNullReference(offset, simpleInstruction.stackPopCount(clazz) - 1)) + { + // In case we detected a certain access to a null array, and OPTIMIZE.CONSERVATIVELY + // is enabled, replace the instruction by the explicit exception. + replaceByException(clazz, offset, simpleInstruction, "java/lang/NullPointerException"); + } break; } } @@ -339,12 +491,27 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c { switch (constantInstruction.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: + if (SideEffectInstructionChecker.OPTIMIZE_CONSERVATIVELY && + isNullReference(offset, constantInstruction.stackPopCount(clazz) - 1)) + { + // In case a method is invoked on a null reference + // replace the instruction with an explicit NullPointerException. + // This is mainly needed to counter obfuscated code that might + // use exceptions to change the control flow. This is especially + // problematic if it happens with methods that are explicitly marked + // as having no side-effect (e.g. String#length()) as they might get + // removed otherwise. + replaceByException(clazz, offset, constantInstruction, "java/lang/NullPointerException"); + break; + } + // intended fallthrough + + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_INVOKESTATIC: if (constantInstruction.stackPushCount(clazz) > 0 && !sideEffectInstructionChecker.hasSideEffects(clazz, method, @@ -761,8 +928,8 @@ private void replaceReferencePushInstruction(Clazz clazz, int offset, Instruction instruction) { - Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); - if (pushedValue.isParticular()) + ReferenceValue pushedValue = partialEvaluator.getStackAfter(offset).getTop(0).referenceValue(); + if (pushedValue.isNull() == Value.ALWAYS) { // A reference value can only be specific if it is null. replaceConstantPushInstruction(clazz, @@ -948,8 +1115,7 @@ private void replaceSimpleEnumSwitchInstruction(Clazz clazz, for (int index = 0; index < newJumpOffsets.length; index++) { int switchCase = - mappingValue.integerArrayLoad(valueFactory.createIntegerValue( - index), + mappingValue.integerArrayLoad(valueFactory.createIntegerValue(index), valueFactory).value(); newJumpOffsets[index] = @@ -1236,6 +1402,96 @@ private void trimSwitchInstruction(Clazz clazz, } + /** + * Checks whether if the current top value on the stack is a divisor + * leading to a certain division by zero for the given computation type. + */ + private boolean isDivisionByZero(int offset, int computationType) + { + TracedStack tracedStack = partialEvaluator.getStackBefore(offset); + Value divisor = tracedStack.getTop(0); + switch (computationType) + { + case Value.TYPE_INTEGER: + return divisor.computationalType() == Value.TYPE_INTEGER && + divisor.isParticular() && + divisor.integerValue().value() == 0; + + case Value.TYPE_LONG: + return divisor.computationalType() == Value.TYPE_LONG && + divisor.isParticular() && + divisor.longValue().value() == 0L; + + case Value.TYPE_FLOAT: + return divisor.computationalType() == Value.TYPE_FLOAT && + divisor.isParticular() && + divisor.floatValue().value() == 0f; + + case Value.TYPE_DOUBLE: + return divisor.computationalType() == Value.TYPE_DOUBLE && + divisor.isParticular() && + divisor.doubleValue().value() == 0d; + + default: + return false; + } + } + + + /** + * Checks whether the value at the given stack entry index is always a null reference. + */ + private boolean isNullReference(int offset, int popStackEntryIndex) + { + TracedStack tracedStack = partialEvaluator.getStackBefore(offset); + Value objectRef = tracedStack.getTop(popStackEntryIndex); + + return objectRef.computationalType() == Value.TYPE_REFERENCE && + objectRef.isParticular() && + objectRef.referenceValue().isNull() == Value.ALWAYS; + } + + + /** + * Replaces the given instruction by an explicit exception. + */ + private void replaceByException(Clazz clazz, + int offset, + Instruction instruction, + String exceptionClass) + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor((ProgramClass)clazz); + + // Replace the instruction by an infinite loop. + Instruction[] replacementInstructions = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_NEW, + constantPoolEditor.addClassConstant(exceptionClass, null)), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, + constantPoolEditor.addMethodrefConstant(exceptionClass, "", "()V", null, null)), + new SimpleInstruction(InstructionConstants.OP_ATHROW) + }; + + if (DEBUG) System.out.println(" Replacing instruction by explicit exception "+exceptionClass); + + codeAttributeEditor.replaceInstruction(offset, replacementInstructions); + + // 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 given instruction by an infinite loop. */ diff --git a/core/src/proguard/optimize/evaluation/InitializationFinder.java b/core/src/proguard/optimize/evaluation/InitializationFinder.java new file mode 100644 index 000000000..f20699426 --- /dev/null +++ b/core/src/proguard/optimize/evaluation/InitializationFinder.java @@ -0,0 +1,349 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.instruction.InstructionFactory; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.evaluation.BasicInvocationUnit; +import proguard.evaluation.value.*; +import proguard.util.ArrayUtil; + +/** + * This AttributeVisitor links 'new' instructions and their corresponding + * initializers in the CodeAttribute objects that it visits. + * + * @author Eric Lafortune + */ +public class InitializationFinder +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("if") != null; + //*/ + + public static final int NONE = -1; + + private final PartialEvaluator partialEvaluator; + private final boolean runPartialEvaluator; + + private int superInitializationOffset; + private int[] initializationOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; + private InstructionOffsetValue[] uninitializedOffsets = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH]; + + + /** + * Creates a new InitializationFinder. + */ + public InitializationFinder() + { + this(new ReferenceTracingValueFactory(new BasicValueFactory())); + } + + + /** + * Creates a new InitializationFinder. This private constructor gets around + * the constraint that it's not allowed to add statements before calling + * 'this'. + */ + private InitializationFinder(ReferenceTracingValueFactory referenceTracingValueFactory) + { + this(new PartialEvaluator(referenceTracingValueFactory, + new ReferenceTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory)), + true, + referenceTracingValueFactory), + true); + } + + + /** + * Creates a new InitializationFinder that will use the given partial + * evaluator. + * @param partialEvaluator the evaluator to be used for the analysis. + * @param runPartialEvaluator specifies whether to run this evaluator on + * every code attribute that is visited. + */ + public InitializationFinder(PartialEvaluator partialEvaluator, + boolean runPartialEvaluator) + { + this.partialEvaluator = partialEvaluator; + this.runPartialEvaluator = runPartialEvaluator; + } + + + /** + * 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 a 'new' +// * instruction. +// */ +// 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 creationOffset) +// { +// return initializationOffsets[creationOffset]; +// } + + + /** + * Returns the 'new' instruction offset at which the object instance is + * created that is initialized at the given offset. + */ + public int creationOffset(int initializationOffset) + { + return creationOffsetValue(initializationOffset).instructionOffset(0); + } + + + /** + * Returns whether the specified stack entry is initialized. + */ + public boolean isInitializedBefore(int offset, int stackEntryIndexBottom) + { + InstructionOffsetValue creationOffsetValue = + creationOffsetValue(offset, stackEntryIndexBottom); + + return isInitializedBefore(offset, creationOffsetValue); + } + + + /** + * Returns whether the given creation offset is initialized before the given + * offset. + */ + public boolean isInitializedBefore(int offset, + InstructionOffsetValue creationOffsetValue) + { + return !uninitializedOffsets[offset].contains(creationOffsetValue.instructionOffset(0)); + } + + + /** + * Returns whether the instruction at the given offset is the special + * invocation of an instance initializer. + */ + public boolean isInitializer(int offset) + { + return partialEvaluator.isInitializer(offset); + } + + + // 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"); + + int codeLength = codeAttribute.u4codeLength; + + superInitializationOffset = NONE; + + // Make sure the global arrays are sufficiently large. + initializationOffsets = ArrayUtil.ensureArraySize(initializationOffsets, codeLength, NONE); + uninitializedOffsets = ArrayUtil.ensureArraySize(uninitializedOffsets,codeLength, null); + + // Evaluate the method. + if (runPartialEvaluator) + { + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + } + + // Loop over all instructions. This is sufficient, because the JVM + // specifications don't allow uninitialized instances on the stack or + // in variables when branching backward. JVMs without preverification + // and the Dalvik VM do allow it in practice. + InstructionOffsetValue currentUninitializedOffsets = method.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT) ? + new InstructionOffsetValue(InstructionOffsetValue.METHOD_PARAMETER) : + InstructionOffsetValue.EMPTY_VALUE; + + for (int offset = 0; offset < codeLength; offset++) + { + if (partialEvaluator.isTraced(offset)) + { + // Exception handlers start without uninitialized instances + // (on the stack or in variables). + if (partialEvaluator.isExceptionHandler(offset)) + { + currentUninitializedOffsets = InstructionOffsetValue.EMPTY_VALUE; + } + + // Check if the uninitialized creation offsets have been set + // before (because of a forward branch). + if (uninitializedOffsets[offset] != null) + { + // Continue using them. + currentUninitializedOffsets = uninitializedOffsets[offset]; + } + else + { + uninitializedOffsets[offset] = currentUninitializedOffsets; + } + + // Is it a 'new' instruction? + if (partialEvaluator.isCreation(offset)) + { + // Add its offset to the current list. + currentUninitializedOffsets = + currentUninitializedOffsets.add(offset); + } + // Is it an instance initialization? + else if (partialEvaluator.isInitializer(offset)) + { + // Remove its creation offset from the current list. + InstructionOffsetValue creationOffsetValue = + creationOffsetValue(offset); + + int creationOffset = + creationOffsetValue.instructionOffset(0); + + if (creationOffsetValue.isMethodParameter(0)) + { + // Remember the super initialization offset of the + // initializer method. + superInitializationOffset = offset; + } + else + { + // Remember the instance initialization for the 'new' + // instruction. + initializationOffsets[creationOffset] = offset; + } + + currentUninitializedOffsets = + currentUninitializedOffsets.remove(creationOffset); + } + + // Propagate the uninitialized creation offsets to the forward + // branch targets, if any. + InstructionOffsetValue branchTargets = + partialEvaluator.branchTargets(offset); + + if (branchTargets != null) + { + for (int branchIndex = 0; branchIndex < branchTargets.instructionOffsetCount(); branchIndex++) + { + int branchOffset = branchTargets.instructionOffset(branchIndex); + if (branchOffset > offset) + { + uninitializedOffsets[branchOffset] = currentUninitializedOffsets; + } + } + + currentUninitializedOffsets = InstructionOffsetValue.EMPTY_VALUE; + } + } + } + + if (DEBUG) + { + System.out.println(); + System.out.println("InitializationFinder: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + + for (int offset = 0; offset < codeLength; offset++) + { + if (partialEvaluator.isInstruction(offset)) + { + System.out.println((initializationOffsets[offset] >= 0 ? "i"+initializationOffsets[offset] : " ") + + (uninitializedOffsets[offset] != null && + uninitializedOffsets[offset].instructionOffsetCount() > 0 ? " u"+uninitializedOffsets[offset] : " ") + + (isInitializer(offset) ? " "+creationOffsetValue(offset) : " ") + " " + + InstructionFactory.create(codeAttribute.code, offset).toString(offset)); + } + } + } + } + + + // Small utility methods. + + /** + * Returns the 'new' instruction offset value (or method parameter) at + * which the object instance is created that is initialized at the given + * offset. + */ + private InstructionOffsetValue creationOffsetValue(int initializationOffset) + { + int stackEntryIndexBottom = + partialEvaluator.getStackAfter(initializationOffset).size(); + + return creationOffsetValue(initializationOffset, stackEntryIndexBottom); + } + + + /** + * Returns the 'new' instruction offset value (or method parameter) of + * the specified stack entry. + */ + private InstructionOffsetValue creationOffsetValue(int instructionOffset, + int stackEntryIndexBottom) + { + // Get the reference value of the new instance. + ReferenceValue newReferenceValue = + partialEvaluator.getStackBefore(instructionOffset).getBottom(stackEntryIndexBottom).referenceValue(); + + // It's a traced reference. + TracedReferenceValue tracedReferenceValue = + (TracedReferenceValue)newReferenceValue; + + // Get the trace value. + return tracedReferenceValue.getTraceValue().instructionOffsetValue(); + } +} diff --git a/core/src/proguard/optimize/evaluation/InstructionUsageMarker.java b/core/src/proguard/optimize/evaluation/InstructionUsageMarker.java new file mode 100644 index 000000000..00066a49d --- /dev/null +++ b/core/src/proguard/optimize/evaluation/InstructionUsageMarker.java @@ -0,0 +1,1731 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.evaluation.TracedStack; +import proguard.evaluation.value.*; +import proguard.optimize.info.*; +import proguard.util.ArrayUtil; + +import java.util.*; + +/** + * This AttributeVisitor marks necessary instructions in the code attributes + * that it visits, based on partial evaluation. + * + * @author Eric Lafortune + */ +public class InstructionUsageMarker +extends SimplifiedVisitor +implements AttributeVisitor +{ + //* + private static final boolean DEBUG = false; + private static final boolean DEBUG_RESULTS = false; + /*/ + private static boolean DEBUG = System.getProperty("ium") != null; + private static boolean DEBUG_RESULTS = DEBUG; + //*/ + + private final PartialEvaluator partialEvaluator; + private final boolean runPartialEvaluator; + private final PartialEvaluator simplePartialEvaluator = new PartialEvaluator(new TypedReferenceValueFactory()); + private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); + private final MyParameterUsageMarker parameterUsageMarker = new MyParameterUsageMarker(); + private final MyInitialUsageMarker initialUsageMarker = new MyInitialUsageMarker(); + private final MyProducerMarker producerMarker = new MyProducerMarker(); + private final MyVariableInitializationMarker variableInitializationMarker = new MyVariableInitializationMarker(); + private final MyStackConsistencyMarker stackConsistencyMarker = new MyStackConsistencyMarker(); + private final MyExtraPopInstructionMarker extraPopInstructionMarker = new MyExtraPopInstructionMarker(); + + private InstructionOffsetValue[] reverseDependencies = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH]; + + private boolean[][] stacksNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; + private boolean[][] stacksUnwantedBefore = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; + private boolean[] instructionsNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + private boolean[] extraPushPopInstructionsNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + + private int maxMarkedOffset; + + + /** + * Creates a new InstructionUsageMarker. + */ + public InstructionUsageMarker() + { + this(new PartialEvaluator(), true); + } + + + /** + * Creates a new InstructionUsageMarker. + * @param partialEvaluator the evaluator to be used for the analysis. + * @param runPartialEvaluator specifies whether to run this evaluator on + * every code attribute that is visited. + */ + public InstructionUsageMarker(PartialEvaluator partialEvaluator, + boolean runPartialEvaluator) + { + this.partialEvaluator = partialEvaluator; + this.runPartialEvaluator = runPartialEvaluator; + } + + + /** + * Returns whether the specified instruction was traced in the most + * recently analyzed code attribute. + */ + public boolean isTraced(int instructionOffset) + { + return partialEvaluator.isTraced(instructionOffset); + } + + + /** + * Returns a filtering version of the given instruction visitor that only + * visits traced instructions. + */ + public InstructionVisitor tracedInstructionFilter(InstructionVisitor instructionVisitor) + { + return partialEvaluator.tracedInstructionFilter(instructionVisitor); + } + + + /** + * Returns a filtering version of the given instruction visitor that only + * visits traced or untraced instructions. + */ + public InstructionVisitor tracedInstructionFilter(boolean traced, + InstructionVisitor instructionVisitor) + { + return partialEvaluator.tracedInstructionFilter(traced, instructionVisitor); + } + + + /** + * Returns whether the specified instruction is necessary in the most + * recently analyzed code attribute. + */ + public boolean isInstructionNecessary(int instructionOffset) + { + return instructionsNecessary[instructionOffset]; + } + + + /** + * Returns whether an extra push/pop instruction is required at the given + * offset in the most recently analyzed code attribute. + */ + public boolean isExtraPushPopInstructionNecessary(int instructionOffset) + { + return extraPushPopInstructionsNecessary[instructionOffset]; + } + + + /** + * Returns a filtering version of the given instruction visitor that only + * visits necessary instructions. + */ + public InstructionVisitor necessaryInstructionFilter(InstructionVisitor instructionVisitor) + { + return necessaryInstructionFilter(true, instructionVisitor); + } + + + /** + * Returns a filtering version of the given instruction visitor that only + * visits necessary or unnecessary instructions. + */ + public InstructionVisitor necessaryInstructionFilter(boolean necessary, + InstructionVisitor instructionVisitor) + { + return new MyNecessaryInstructionFilter(necessary, instructionVisitor); + } + + + /** + * Returns the stack before execution of the instruction at the given + * offset. + */ + public TracedStack getStackBefore(int instructionOffset) + { + return partialEvaluator.getStackBefore(instructionOffset); + } + + + /** + * Returns the stack after execution of the instruction at the given + * offset. + */ + public TracedStack getStackAfter(int instructionOffset) + { + return partialEvaluator.getStackAfter(instructionOffset); + } + + + /** + * Returns whether the specified stack entry before the given offset is + * unwanted, e.g. because it was intended as a method parameter that has + * been removed. + */ + public boolean isStackEntryUnwantedBefore(int instructionOffset, + int stackIndex) + { + return stacksUnwantedBefore[instructionOffset][stackIndex]; + } + + + /** + * Returns whether the stack specified entries before the given offset are + * present. + */ + public 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). + */ + public 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. + */ + public 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). + */ + public boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets, + int stackIndex) + { + int offsetCount = instructionOffsets.instructionOffsetCount(); + + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + if (instructionOffsets.isExceptionHandler(offsetIndex) || + 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). + */ + public boolean isStackEntryNecessaryAfter(int instructionOffset, + int stackIndex) + { + return + (instructionOffset & InstructionOffsetValue.EXCEPTION_HANDLER) != 0 || + stacksNecessaryAfter[instructionOffset][stackIndex]; + } + + + /** + * Returns the instruction offsets to which the given instruction offset + * branches in the most recently analyzed code attribute. + */ + public InstructionOffsetValue branchTargets(int instructionOffset) + { + return partialEvaluator.branchTargets(instructionOffset); + } + + + // 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 instruction usage marker 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 marking instruction usage 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()+")"); + + 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("InstructionUsageMarker ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + } + + // Initialize the necessary arrays. + initializeNecessary(codeAttribute); + + // Evaluate the method. + if (runPartialEvaluator) + { + 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; + + maxMarkedOffset = -1; + + // Mark any unused method parameters on the stack. + if (DEBUG) System.out.println("Invocation simplification:"); + + codeAttribute.instructionsAccept(clazz, method, + partialEvaluator.tracedInstructionFilter(parameterUsageMarker)); + + + // Mark all essential instructions that have been encountered as used. + // Also mark infinite loops and instructions that can have side effects. + if (DEBUG) System.out.println("Usage initialization: "); + + codeAttribute.instructionsAccept(clazz, method, + partialEvaluator.tracedInstructionFilter(initialUsageMarker)); + + 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)) + { + // Mark the stack/variable producers of this instruction/ + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, producerMarker); + + // Also mark any reverse dependencies. + markReverseDependencies(offset); + } + + // Check if this instruction is a branch origin from a branch + // that straddles some marked code. + markStraddlingBranches(offset, + partialEvaluator.branchTargets(offset), + true); + + // Check if this instruction is a branch target from a branch + // that straddles some marked code. + markStraddlingBranches(offset, + partialEvaluator.branchOrigins(offset), + false); + } + + 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: "); + + codeAttribute.instructionsAccept(clazz, method, + necessaryInstructionFilter( + variableInitializationMarker)); + + if (DEBUG) System.out.println(); + + + // Mark produced stack entries, 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, stackConsistencyMarker); + + // 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(); + + + // Mark unnecessary popping instructions, in order to keep the stack + // consistent. + if (DEBUG) System.out.println("Extra pop marking:"); + + maxMarkedOffset = codeLength - 1; + + while (maxMarkedOffset >= 0) + { + int offset = maxMarkedOffset; + + maxMarkedOffset = offset - 1; + + if (partialEvaluator.isTraced(offset) && + !isInstructionNecessary(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, extraPopInstructionMarker); + + // 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(); + + + if (DEBUG_RESULTS) + { + System.out.println("Instruction usage results:"); + + int offset = 0; + do + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + System.out.println((isInstructionNecessary(offset) ? " + " : + isExtraPushPopInstructionNecessary(offset) ? " ~ " : + " - ") + + instruction.toString(offset)); + + offset += instruction.length(offset); + } + while (offset < codeLength); + } + } + + + /** + * This MemberVisitor marks stack entries that aren't necessary because + * parameters aren't used in the methods that are visited. + */ + private class MyParameterUsageMarker + extends SimplifiedVisitor + implements InstructionVisitor, + ConstantVisitor, + MemberVisitor + { + private int parameterSize; + private long usedParameters; + + + // 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: + { + parameterSize = 0; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + + // Mark unused parameters. + for (int index = 0; index < parameterSize; index++) + { + if (index < 64 && + (usedParameters & (1L << index)) == 0L) + { + TracedStack stack = + partialEvaluator.getStackBefore(offset); + + int stackIndex = stack.size() - parameterSize + index; + + if (DEBUG) + { + System.out.println(" ["+offset+"] Ignoring parameter #"+index+" (stack entry #"+stackIndex+" ["+stack.getBottom(stackIndex)+"])"); + System.out.println(" Full stack: "+stack); + } + + markStackEntryUnwantedBefore(offset, stackIndex); + } + } + 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 and the mask of the used + // parameters. + parameterSize = ParameterUsageMarker.getParameterSize(programMethod); + usedParameters = ParameterUsageMarker.getUsedParameters(programMethod); + } + } + + + /** + * This InstructionVisitor marks the instructions that are intrinsically + * necessary, because they have side effects. + */ + private class MyInitialUsageMarker + extends SimplifiedVisitor + implements InstructionVisitor, + ConstantVisitor, + ParameterVisitor + { + private final MemberVisitor reverseDependencyCreator = new AllParameterVisitor(true, this); + + // Parameters and values for visitor methods. + private int referencingOffset; + private int referencingPopCount; + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + if (sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + instruction)) + { + markInstruction(offset); + } + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + 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: + createReverseDependencies(clazz, offset, simpleInstruction); + + // Also check for side-effects of the instruction itself. + visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + break; + + default: + visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + break; + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_ANEWARRAY: + case InstructionConstants.OP_MULTIANEWARRAY: + // We may have to mark the instruction due to initializers. + referencingOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + + // Also check for side-effects of the instruction itself. + visitAnyInstruction(clazz, method, codeAttribute, offset, constantInstruction); + break; + + case InstructionConstants.OP_LDC: + case InstructionConstants.OP_LDC_W: + case InstructionConstants.OP_NEW: + case InstructionConstants.OP_GETSTATIC: + // We may have to mark the instruction due to initializers. + referencingOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + + case InstructionConstants.OP_PUTFIELD: + createReverseDependencies(clazz, offset, constantInstruction); + break; + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + referencingOffset = offset; + referencingPopCount = constantInstruction.stackPopCount(clazz); + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + + default: + visitAnyInstruction(clazz, method, codeAttribute, offset, constantInstruction); + break; + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + if (branchInstruction.opcode == InstructionConstants.OP_GOTO && + branchInstruction.branchOffset == 0) + { + if (DEBUG) System.out.print("(infinite loop)"); + markInstruction(offset); + } + else + { + visitAnyInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + Clazz referencedClass = stringConstant.referencedClass; + + // If a static initializer may have side effects, the instruction + // has to be marked. + if (referencedClass != null && + SideEffectClassChecker.mayHaveSideEffects(clazz, + referencedClass)) + { + // Mark the invocation. + markInstruction(referencingOffset); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + Clazz referencedClass = classConstant.referencedClass; + + // If a static initializer may have side effects, the instruction + // has to be marked. + if (referencedClass == null || + SideEffectClassChecker.mayHaveSideEffects(clazz, + referencedClass)) + { + // Mark the invocation. + markInstruction(referencingOffset); + } + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + clazz.constantPoolEntryAccept(fieldrefConstant.u2classIndex, this); + } + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + Method referencedMethod = (Method)refConstant.referencedMember; + +// if (referencedMethod != null) +// { +// System.out.println("InstructionUsageMarker$MyInitialUsageMarker.visitAnyMethodrefConstant [" + refConstant.getClassName(clazz) + "." + refConstant.getName(clazz) + +// "]: mark! esc = " + ParameterEscapeMarker.getEscapingParameters(referencedMethod) + +// ", mod = " + ParameterEscapeMarker.modifiesAnything(referencedMethod) + +// ", side = " + SideEffectClassChecker.mayHaveSideEffects(clazz, +// refConstant.referencedClass, +// referencedMethod)); +// } + + // Is the method invocation really necessary? + if (SideEffectInstructionChecker.OPTIMIZE_CONSERVATIVELY && + referencedMethod != null && + SideEffectMethodMarker.hasSideEffects(referencedMethod) && + // Skip if the method was explicitly marked as having no external side-effects. + !NoExternalSideEffectMethodMarker.hasNoExternalSideEffects(referencedMethod)) + { + // In case we shall optimize conservatively, always mark the method + // call if the referenced method has side effects. + markInstruction(referencingOffset); + } + else if (referencedMethod == null || + ParameterEscapeMarker.getEscapingParameters(referencedMethod) != 0L || + ParameterEscapeMarker.modifiesAnything(referencedMethod) || + SideEffectClassChecker.mayHaveSideEffects(clazz, + refConstant.referencedClass, + referencedMethod)) + { +// System.out.println(" -> mark ["+referencingOffset+"]"); + // Mark the invocation. + markInstruction(referencingOffset); + } + else + { + if (DEBUG) + { + System.out.println(" ["+referencingOffset+"] Checking parameters of ["+refConstant.getClassName(clazz)+"."+refConstant.getName(clazz)+refConstant.getType(clazz)+"] (pop count = "+referencingPopCount+")"); + } + + // Create reverse dependencies for reference parameters that + // are modified. + refConstant.referencedMemberAccept(reverseDependencyCreator); + } + } + + + // Implementations for ParameterVisitor. + + public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) + { + Method method = (Method)member; + + if (DEBUG) + { + System.out.println(" P"+parameterIndex+ + ": escaping = "+ParameterEscapeMarker.isParameterEscaping(method, parameterIndex)+ + ", modified = "+ParameterEscapeMarker.isParameterModified(method, parameterIndex)+ + ", returned = "+ParameterEscapeMarker.isParameterReturned(method, parameterIndex)); + } + + // Create a reverse dependency if the reference parameter is + // modified. + if (ParameterEscapeMarker.isParameterModified(method, parameterIndex)) + { + createReverseDependencies(referencingOffset, + parameterSize - parameterOffset - 1); + } + } + + + /** + * Marks the specified instruction offset or creates reverse + * dependencies to the producers of its bottom popped stack entry. + */ + private void createReverseDependencies(Clazz clazz, + int offset, + Instruction instruction) + { + createReverseDependencies(offset, + instruction.stackPopCount(clazz) - 1); + } + + + /** + * Marks the specified instruction offset or creates reverse + * dependencies to the producers of the specified stack entry, if it + * is a reference value. + */ + private void createReverseDependencies(int offset, + int stackEntryIndex) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(offset); + Value stackEntry = stackBefore.getTop(stackEntryIndex); +// System.out.println(" ["+offset+"] s"+stackEntryIndex+": ["+stackEntry+"]"); + + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + ReferenceValue referenceValue = stackEntry.referenceValue(); +// System.out.println("EvaluationShrinker$MyInitialUsageMarker.createReverseDependencies: ["+offset+"] ["+referenceValue+"]?"); + // The null reference value may not have a trace value. + if (referenceValue.isNull() != Value.ALWAYS) + { + if (referenceValue instanceof TracedReferenceValue) + { + TracedReferenceValue tracedReferenceValue = + (TracedReferenceValue)referenceValue; + + createReverseDependencies(offset, + tracedReferenceValue.getTraceValue().instructionOffsetValue()); + } + else + { +// System.out.println("InstructionUsageMarker$MyInitialUsageMarker.createReverseDependencies: not a TracedReferenceValue"); + markInstruction(offset); + } + } + } + } + + + /** + * Marks the specified instruction offset or creates reverse + * dependencies to the producers of the given reference value. + */ + private void createReverseDependencies(int offset, + InstructionOffsetValue producerOffsets) + { + InstructionOffsetValue consumerOffset = + new InstructionOffsetValue(offset); + + int offsetCount = producerOffsets.instructionOffsetCount(); + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + if (producerOffsets.isNewinstance(offsetIndex)) + { + // Create a reverse dependency. If the creating instruction + // is necessary, then so is this one. + int producerOffset = producerOffsets.instructionOffset(offsetIndex); + + // Avoid circular dependencies in code that loops with + // instances on the stack (like the string encryption code). + if (producerOffset != offset) + { + if (DEBUG) System.out.println(" Inserting reverse dependency from instance producers ["+producerOffset+"] to ["+offset+"]"); + + InstructionOffsetValue reverseDependency = + reverseDependencies[producerOffset]; + + reverseDependencies[producerOffset] = + reverseDependency == null ? + consumerOffset : + reverseDependency.generalize(consumerOffset); + } + } + else + { + // Just mark the instruction. + markInstruction(offset); + } + } + } + } + + + /** + * 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) + { + 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. + markVariableInitializersBefore(offset, variableInstruction.variableIndex, null); + } + } + } + + + /** + * This InstructionVisitor marks stack entries that should be pushed + * (and previously unnecessary pushing instructions) to keep the stack + * consistent at later points in the execution. + */ + private class MyStackConsistencyMarker + extends SimplifiedVisitor + implements InstructionVisitor + { + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // We check all entries to make sure the stack is also consistent + // at method exit points, where some stack entries might be + // discarded. + int stackSize = partialEvaluator.getStackBefore(offset).size(); + + for (int stackIndex = 0; stackIndex < stackSize; stackIndex++) + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (!isStackEntryUnwantedBefore(offset, stackIndex) && + isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex, false); + } + } + } + } + + + /** + * This InstructionVisitor marks unnecessary popping instructions that + * should still pop some values to keep the stack consistent. + */ + private class MyExtraPopInstructionMarker + extends SimplifiedVisitor + implements InstructionVisitor + { + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Check all stack entries that are popped. + // + // Typical case: a stack value that is required elsewhere or a + // pushed exception type that still has to be popped. + int stackSize = partialEvaluator.getStackBefore(offset).size(); + + int firstStackIndex = + stackSize - instruction.stackPopCount(clazz); + + for (int stackIndex = firstStackIndex; stackIndex < stackSize; stackIndex++) + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (!isStackEntryUnwantedBefore(offset, stackIndex) && + isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark that we'll need an extra pop instruction. + markExtraPushPopInstruction(offset); + } + } + } + } + + + // 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++) + { + if (!producerOffsets.isMethodParameter(offsetIndex) && + !producerOffsets.isExceptionHandler(offsetIndex)) + { + // Make sure the variable and the instruction are marked + // at the producing offset. + int offset = producerOffsets.instructionOffset(offsetIndex); + + markInstruction(offset); + } + } + } + } + + + /** + * Ensures that the given variable is initialized before the specified + * consumer of that variable, in the JVM's view. + * @param consumerOffset the instruction offset before which the variable + * needs to be initialized. + * @param variableIndex the index of the variable. + * @param visitedOffsets the already visited consumer offsets, needed to + * prevent infinite loops. + */ + private void markVariableInitializersBefore(int consumerOffset, + int variableIndex, + InstructionOffsetValue visitedOffsets) + { + // Avoid infinite loops by stopping recursion if we encounter + // an already visited offset. + if (visitedOffsets != null && + visitedOffsets.contains(consumerOffset)) + { + return; + } + + visitedOffsets = visitedOffsets == null ? + new InstructionOffsetValue(consumerOffset) : + visitedOffsets.add(consumerOffset); + + // Make sure the variable is initialized after all producers. + // Use the simple evaluator, to get the JVM's view of what is + // initialized. + InstructionOffsetValue producerOffsets = + simplePartialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); + + int offsetCount = producerOffsets.instructionOffsetCount(); + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + if (!producerOffsets.isMethodParameter(offsetIndex) && + !producerOffsets.isExceptionHandler(offsetIndex)) + { + int producerOffset = + producerOffsets.instructionOffset(offsetIndex); + markVariableInitializersAfter(producerOffset, variableIndex, visitedOffsets); + } + } + } + + + /** + * Ensures that the given variable is initialized after the specified + * producer of that variable, in the JVM's view. + * @param producerOffset the instruction offset after which the variable + * needs to be initialized. + * @param variableIndex the index of the variable. + * @param visitedOffsets the already visited consumer offsets, needed to + * prevent infinite loops. + */ + private void markVariableInitializersAfter(int producerOffset, + int variableIndex, + InstructionOffsetValue visitedOffsets) + { + // No problem if the producer has already been marked. + if (!isInstructionNecessary(producerOffset)) + { + // Is the unmarked producer a variable initialization? + if (isVariableInitialization(producerOffset, variableIndex)) + { + // Mark the producer. + if (DEBUG) System.out.print(" Marking initialization of v"+variableIndex+" at "); + + markInstruction(producerOffset); + + if (DEBUG) System.out.println(); + } + else + { + // Don't mark the producer, but recursively look at the + // preceding producers of the same variable. Their values + // will fall through, replacing this producer. + markVariableInitializersBefore(producerOffset, variableIndex, visitedOffsets); + } + } + } + + + /** + * 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, true); + } + } + + + /** + * 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, true); + } + } + + + /** + * Marks the stack entry and optionally 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). + * @param markInstructions specifies whether the producing instructions + * should be marked. + */ + private void markStackEntryProducers(int consumerOffset, + int stackIndex, + boolean markInstructions) + { + if (!isStackEntryUnwantedBefore(consumerOffset, stackIndex)) + { + markStackEntryProducers(partialEvaluator.getStackBefore(consumerOffset).getBottomProducerValue(stackIndex).instructionOffsetValue(), + stackIndex, + markInstructions); + } + } + + + /** + * Marks the stack entry and optionally 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). + * @param markInstructions specifies whether the producing instructions + * should be marked. + */ + private void markStackEntryProducers(InstructionOffsetValue producerOffsets, + int stackIndex, + boolean markInstructions) + { + if (producerOffsets != null) + { + int offsetCount = producerOffsets.instructionOffsetCount(); + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + if (!producerOffsets.isExceptionHandler(offsetIndex)) + { + // Make sure the stack entry and the instruction are marked + // at the producing offset. + int offset = producerOffsets.instructionOffset(offsetIndex); + + markStackEntryAfter(offset, stackIndex); + + if (markInstructions) + { + markInstruction(offset); + } + else + { + markExtraPushPopInstruction(offset); + } + } + } + } + } + + + /** + * Marks any modification instructions that are required by the specified + * creation instruction (new, newarray, method returning new + * instance,...), so this new instance is properly initialized. + * @param instructionOffset the offset of the creation instruction. + */ + private void markReverseDependencies(int instructionOffset) + { + InstructionOffsetValue reverseDependency = + reverseDependencies[instructionOffset]; + + if (reverseDependency != null) + { + markInstructions(reverseDependency); + } + } + + + /** + * 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); + } + } + + + /** + * 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. + reverseDependencies = + ArrayUtil.ensureArraySize(reverseDependencies, codeLength, null); + + 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 (stacksUnwantedBefore.length < codeLength || + stacksUnwantedBefore[0].length < maxStack) + { + stacksUnwantedBefore = new boolean[codeLength][maxStack]; + } + else + { + for (int offset = 0; offset < codeLength; offset++) + { + Arrays.fill(stacksUnwantedBefore[offset], 0, maxStack, false); + } + } + + instructionsNecessary = + ArrayUtil.ensureArraySize(instructionsNecessary, + codeLength, + false); + + extraPushPopInstructionsNecessary = + ArrayUtil.ensureArraySize(extraPushPopInstructionsNecessary, + 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 = simplePartialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex); + if (valueBefore == null) + { + return true; + } + + // Is the computational type different now? + Value valueAfter = simplePartialEvaluator.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)? + InstructionOffsetValue producersBefore = simplePartialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex).instructionOffsetValue(); + return producersBefore.instructionOffsetCount() == 1 && + producersBefore.isMethodParameter(0); + } + + + /** + * 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; + } + } + } + + + + /** + * Marks the specified stack entry as unwanted, typically because it is + * an unused parameter of a method invocation. + * @param instructionOffset the offset of the consumer. + * @param stackIndex the index of the stack entry to be marked + * (counting from the bottom). + */ + private void markStackEntryUnwantedBefore(int instructionOffset, + int stackIndex) + { + stacksUnwantedBefore[instructionOffset][stackIndex] = true; + } + + + private void markInstructions(InstructionOffsetValue instructionOffsets) + { + int count = instructionOffsets.instructionOffsetCount(); + + for (int index = 0; index < count; index++) + { + markInstruction(instructionOffsets.instructionOffset(index)); + } + } + + + private void markInstruction(int instructionOffset) + { + if (!isInstructionNecessary(instructionOffset)) + { + if (DEBUG) System.out.print(instructionOffset+","); + + instructionsNecessary[instructionOffset] = true; + + if (maxMarkedOffset < instructionOffset) + { + maxMarkedOffset = instructionOffset; + } + } + } + + + private void markExtraPushPopInstruction(int instructionOffset) + { + if (!isInstructionNecessary(instructionOffset) && + !isExtraPushPopInstructionNecessary(instructionOffset)) + { + if (DEBUG) System.out.print(instructionOffset+","); + + extraPushPopInstructionsNecessary[instructionOffset] = true; + + if (maxMarkedOffset < instructionOffset) + { + maxMarkedOffset = instructionOffset; + } + } + } + + + private boolean isAnyInstructionNecessary(int instructionOffset1, + int instructionOffset2) + { + for (int instructionOffset = instructionOffset1; + instructionOffset < instructionOffset2; + instructionOffset++) + { + if (isInstructionNecessary(instructionOffset) || + isExtraPushPopInstructionNecessary(instructionOffset)) + { + return true; + } + } + + return false; + } + + + /** + * This InstructionVisitor delegates its visits to a given + * InstructionVisitor, but only if the instruction has been marked as + * necessary (or not). + */ + private class MyNecessaryInstructionFilter implements InstructionVisitor + { + private final boolean necessary; + private final InstructionVisitor instructionVisitor; + + + public MyNecessaryInstructionFilter(boolean necessary, + InstructionVisitor instructionVisitor) + { + this.necessary = necessary; + this.instructionVisitor = instructionVisitor; + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitSimpleInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction); + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitTableSwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction); + } + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitLookUpSwitchInstruction(clazz, method, codeAttribute, offset, lookUpSwitchInstruction); + } + } + + + // Small utility methods. + + /** + * Returns whether the instruction at the given offset should be + * visited, depending on whether it is necessary or not. + */ + private boolean shouldVisit(int offset) + { + return isInstructionNecessary(offset) == necessary; + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/evaluation/LivenessAnalyzer.java b/core/src/proguard/optimize/evaluation/LivenessAnalyzer.java similarity index 76% rename from src/proguard/optimize/evaluation/LivenessAnalyzer.java rename to core/src/proguard/optimize/evaluation/LivenessAnalyzer.java index bfcb62959..5bb977e49 100644 --- a/src/proguard/optimize/evaluation/LivenessAnalyzer.java +++ b/core/src/proguard/optimize/evaluation/LivenessAnalyzer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,7 +26,9 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; +import proguard.evaluation.BasicInvocationUnit; import proguard.evaluation.value.*; +import proguard.util.ArrayUtil; /** * This AttributeVisitor analyzes the liveness of the variables in the code @@ -48,7 +50,10 @@ public class LivenessAnalyzer private static final int MAX_VARIABLES_SIZE = 64; - private final PartialEvaluator partialEvaluator; + private final PartialEvaluator partialEvaluator; + private final boolean runPartialEvaluator; + private final InitializationFinder initializationFinder; + private final boolean runInitializationFinder; private long[] isAliveBefore = new long[ClassConstants.TYPICAL_CODE_LENGTH]; private long[] isAliveAfter = new long[ClassConstants.TYPICAL_CODE_LENGTH]; @@ -64,17 +69,61 @@ public class LivenessAnalyzer */ public LivenessAnalyzer() { - this(new PartialEvaluator()); + this(new ReferenceTracingValueFactory(new BasicValueFactory())); } /** - * Creates a new LivenessAnalyzer that will use the given partial evaluator. - * It will run this evaluator on every code attribute that it visits. + * Creates a new LivenessAnalyzer. This private constructor gets around + * the constraint that it's not allowed to add statements before calling + * 'this'. */ - public LivenessAnalyzer(PartialEvaluator partialEvaluator) + private LivenessAnalyzer(ReferenceTracingValueFactory referenceTracingValueFactory) { - this.partialEvaluator = partialEvaluator; + this(new PartialEvaluator(referenceTracingValueFactory, + new ReferenceTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory)), + true, + referenceTracingValueFactory), + true); + } + + + /** + * Creates a new LivenessAnalyzer. This private constructor gets around + * the constraint that it's not allowed to add statements before calling + * 'this'. + */ + private LivenessAnalyzer(PartialEvaluator partialEvaluator, + boolean runPartialEvaluator) + { + this(partialEvaluator, + runPartialEvaluator, + new InitializationFinder(partialEvaluator, false), + true); + } + + + /** + * Creates a new LivenessAnalyzer that will use the given partial evaluator + * and initialization finder. + * @param partialEvaluator the evaluator to be used for the analysis. + * @param runPartialEvaluator specifies whether to run this evaluator on + * every code attribute that is visited. + * @param initializationFinder the initialization finder to be used for + * the analysis. + * @param runInitializationFinder specifies whether to run this + * initialization finder on every code + * attribute that is visited. + */ + public LivenessAnalyzer(PartialEvaluator partialEvaluator, + boolean runPartialEvaluator, + InitializationFinder initializationFinder, + boolean runInitializationFinder) + { + this.partialEvaluator = partialEvaluator; + this.runPartialEvaluator = runPartialEvaluator; + this.initializationFinder = initializationFinder; + this.runInitializationFinder = runInitializationFinder; } @@ -94,8 +143,9 @@ public boolean isTraced(int instructionOffset) */ public boolean isAliveBefore(int instructionOffset, int variableIndex) { - return variableIndex >= MAX_VARIABLES_SIZE || - (isAliveBefore[instructionOffset] & (1L << variableIndex)) != 0; + return variableIndex >= MAX_VARIABLES_SIZE ? + partialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex) != null : + (isAliveBefore[instructionOffset] & (1L << variableIndex)) != 0; } @@ -125,8 +175,9 @@ public void setAliveBefore(int instructionOffset, int variableIndex, boolean ali */ public boolean isAliveAfter(int instructionOffset, int variableIndex) { - return variableIndex >= MAX_VARIABLES_SIZE || - (isAliveAfter[instructionOffset] & (1L << variableIndex)) != 0; + return variableIndex >= MAX_VARIABLES_SIZE ? + partialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex) != null : + (isAliveAfter[instructionOffset] & (1L << variableIndex)) != 0; } @@ -156,8 +207,10 @@ public void setAliveAfter(int instructionOffset, int variableIndex, boolean aliv */ public boolean isCategory2(int instructionOffset, int variableIndex) { - return variableIndex < MAX_VARIABLES_SIZE && - (isCategory2[instructionOffset] & (1L << variableIndex)) != 0; + return variableIndex >= MAX_VARIABLES_SIZE ? + partialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex) != null && + partialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex).isCategory2() : + (isCategory2[instructionOffset] & (1L << variableIndex)) != 0; } @@ -198,14 +251,24 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt System.out.println("Liveness analysis: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); } + int codeLength = codeAttribute.u4codeLength; + int variablesSize = codeAttribute.u2maxLocals; + // Initialize the global arrays. - initializeArrays(codeAttribute); + isAliveBefore = ArrayUtil.ensureArraySize(isAliveBefore, codeLength, 0L); + isAliveAfter = ArrayUtil.ensureArraySize(isAliveAfter, codeLength, 0L); + isCategory2 = ArrayUtil.ensureArraySize(isCategory2, codeLength, 0L); // Evaluate the method. - partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + if (runPartialEvaluator) + { + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + } - int codeLength = codeAttribute.u4codeLength; - int variablesSize = codeAttribute.u2maxLocals; + if (runInitializationFinder) + { + initializationFinder.visitCodeAttribute(clazz, method, codeAttribute); + } // We'll only really analyze the first 64 variables. if (variablesSize > MAX_VARIABLES_SIZE) @@ -250,7 +313,12 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt isAliveBefore[offset] = alive; // Do we have to check again after this loop? - checkAgain |= offset < maxOffset(partialEvaluator.branchOrigins(offset)); + InstructionOffsetValue branchOrigins = partialEvaluator.branchOrigins(offset); + if (branchOrigins != null && + offset < branchOrigins.maximumValue()) + { + checkAgain = true; + } } } } @@ -374,7 +442,7 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c { // Special case: variable 0 ('this') in an initializer has to be alive // as long as it hasn't been initialized. - if (offset == partialEvaluator.superInitializationOffset()) + if (offset == initializationFinder.superInitializationOffset()) { alive |= 1L; } @@ -413,32 +481,6 @@ public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAtt // 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. @@ -455,72 +497,4 @@ private long combinedLiveness(InstructionOffsetValue instructionOffsetValue) 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/core/src/proguard/optimize/evaluation/LoadingInvocationUnit.java similarity index 84% rename from src/proguard/optimize/evaluation/LoadingInvocationUnit.java rename to core/src/proguard/optimize/evaluation/LoadingInvocationUnit.java index 08a6e50ad..771064a50 100644 --- a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java +++ b/core/src/proguard/optimize/evaluation/LoadingInvocationUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -68,9 +68,9 @@ public LoadingInvocationUnit(ValueFactory valueFactory, // Implementations for BasicInvocationUnit. - protected Value getFieldClassValue(Clazz clazz, - RefConstant refConstant, - String type) + public Value getFieldClassValue(Clazz clazz, + RefConstant refConstant, + String type) { if (loadFieldValues) { @@ -91,9 +91,9 @@ protected Value getFieldClassValue(Clazz clazz, } - protected Value getFieldValue(Clazz clazz, - RefConstant refConstant, - String type) + public Value getFieldValue(Clazz clazz, + RefConstant refConstant, + String type) { if (loadFieldValues) { @@ -114,11 +114,11 @@ protected Value getFieldValue(Clazz clazz, } - protected Value getMethodParameterValue(Clazz clazz, - Method method, - int parameterIndex, - String type, - Clazz referencedClass) + public Value getMethodParameterValue(Clazz clazz, + Method method, + int parameterIndex, + String type, + Clazz referencedClass) { if (loadMethodParameterValues) { @@ -138,9 +138,9 @@ protected Value getMethodParameterValue(Clazz clazz, } - protected Value getMethodReturnValue(Clazz clazz, - RefConstant refConstant, - String type) + public Value getMethodReturnValue(Clazz clazz, + RefConstant refConstant, + String type) { if (loadMethodReturnValues) { diff --git a/core/src/proguard/optimize/evaluation/ParameterTracingInvocationUnit.java b/core/src/proguard/optimize/evaluation/ParameterTracingInvocationUnit.java new file mode 100644 index 000000000..b5dea742d --- /dev/null +++ b/core/src/proguard/optimize/evaluation/ParameterTracingInvocationUnit.java @@ -0,0 +1,172 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.classfile.util.ClassUtil; +import proguard.evaluation.SimplifiedInvocationUnit; +import proguard.evaluation.value.*; +import proguard.optimize.info.ParameterEscapeMarker; + +/** + * This InvocationUnit tags reference values like + * {@link ReferenceTracingInvocationUnit}, but adds detail to return values + * from invoked methods. + * + * @see ReferenceTracingInvocationUnit + * @see TracedReferenceValue + * @see InstructionOffsetValue + * @author Eric Lafortune + */ +public class ParameterTracingInvocationUnit +extends ReferenceTracingInvocationUnit +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("ptiu") != null; + //*/ + + + private Value[] parameters = new Value[256]; + + + /** + * Creates a new ParameterTracingInvocationUnit that attaches trace + * values specifying the parameter index to each parameter. + * @param invocationUnit the invocation unit to which invocations will + * be delegated. + */ + public ParameterTracingInvocationUnit(SimplifiedInvocationUnit invocationUnit) + { + super(invocationUnit); + } + + + // Implementations for SimplifiedInvocationUnit. + + public void setMethodParameterValue(Clazz clazz, RefConstant refConstant, int parameterIndex, Value value) + { + super.setMethodParameterValue(clazz, refConstant, parameterIndex, value); + + parameters[parameterIndex] = value; + } + + + public Value getMethodReturnValue(Clazz clazz, RefConstant refConstant, String type) + { + Value returnValue = + super.getMethodReturnValue(clazz, refConstant, type); + + // We only need to worry about reference values. + if (returnValue.computationalType() != Value.TYPE_REFERENCE) + { + return returnValue; + } + + Method referencedMethod = (Method)refConstant.referencedMember; + if (referencedMethod != null) + { + // Start figuring out which trace value to attach to the return value. + int offset = + ((TracedReferenceValue)returnValue).getTraceValue().instructionOffsetValue().instructionOffset(0); + + // The trace value might be any external value or just a new instance. + InstructionOffsetValue traceValue = + ParameterEscapeMarker.returnsExternalValues(referencedMethod) ? new InstructionOffsetValue(offset | InstructionOffsetValue.FIELD_VALUE) : + ParameterEscapeMarker.returnsNewInstances(referencedMethod) ? new InstructionOffsetValue(offset | InstructionOffsetValue.NEW_INSTANCE) : + null; + + long returnedParameters = + ParameterEscapeMarker.getReturnedParameters(referencedMethod); + + int parameterCount = + ClassUtil.internalMethodParameterCount(refConstant.getType(clazz), isStatic); + + for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++) + { + if ((returnedParameters & (1L << parameterIndex)) != 0L) + { + Value parameterValue = parameters[parameterIndex]; + if (parameterValue instanceof TracedReferenceValue) + { + TracedReferenceValue tracedParameterValue = + (TracedReferenceValue)parameterValue; + + if (mayReturnType(refConstant.referencedClass, + referencedMethod, + tracedParameterValue)) + { + InstructionOffsetValue parameterTraceValue = + tracedParameterValue.getTraceValue().instructionOffsetValue(); + + traceValue = traceValue == null ? + parameterTraceValue : + traceValue.generalize(parameterTraceValue); + } + } + } + } + + if (DEBUG) + { + System.out.println("ParameterTracingInvocationUnit.getMethodReturnValue: calling ["+refConstant.getClassName(clazz)+"."+refConstant.getName(clazz)+refConstant.getType(clazz)+"] returns ["+traceValue+" "+returnValue+"]"); + } + + // Did we find more detailed information on the return value? + // We should, unless the return value is always null. + if (traceValue != null) + { + return trace(returnValue, traceValue); + } + } + + return returnValue; + } + + + // Small utility methods. + + /** + * Returns whether the given method may return the given type of reference + * value + */ + private boolean mayReturnType(Clazz clazz, + Method method, + ReferenceValue referenceValue) + { + String returnType = + ClassUtil.internalMethodReturnType(method.getDescriptor(clazz)); + + Clazz[] referencedClasses = method instanceof ProgramMethod ? + ((ProgramMethod)method).referencedClasses : + ((LibraryMethod)method).referencedClasses; + + Clazz referencedClass = + referencedClasses == null || + !ClassUtil.isInternalClassType(returnType) ? null : + referencedClasses[referencedClasses.length - 1]; + + return referenceValue.instanceOf(returnType, + referencedClass) != Value.NEVER; + } +} \ No newline at end of file diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/core/src/proguard/optimize/evaluation/PartialEvaluator.java similarity index 77% rename from src/proguard/optimize/evaluation/PartialEvaluator.java rename to core/src/proguard/optimize/evaluation/PartialEvaluator.java index 96281d270..28e582699 100644 --- a/src/proguard/optimize/evaluation/PartialEvaluator.java +++ b/core/src/proguard/optimize/evaluation/PartialEvaluator.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,13 +23,13 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.visitor.*; -import proguard.classfile.constant.ClassConstant; 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.evaluation.BranchTargetFinder; +import proguard.optimize.peephole.BranchTargetFinder; import java.util.Arrays; @@ -48,8 +48,8 @@ public class PartialEvaluator private static final boolean DEBUG = false; private static final boolean DEBUG_RESULTS = false; /*/ - private static boolean DEBUG = System.getProperty("pe") != null; - private static boolean DEBUG_RESULTS = DEBUG; + public static boolean DEBUG = System.getProperty("pe") != null; + public static boolean DEBUG_RESULTS = DEBUG; //*/ private static final int MAXIMUM_EVALUATION_COUNT = 5; @@ -58,18 +58,19 @@ public class PartialEvaluator 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 final ValueFactory valueFactory; + private final InvocationUnit invocationUnit; + private final boolean evaluateAllCode; + private final InstructionVisitor extraInstructionVisitor; + + 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 int codeLength; @@ -85,9 +86,7 @@ public class PartialEvaluator */ public PartialEvaluator() { - this(new ValueFactory(), - new BasicInvocationUnit(new ValueFactory()), - true); + this(new BasicValueFactory()); } @@ -95,6 +94,19 @@ public PartialEvaluator() * Creates a new PartialEvaluator. * @param valueFactory the value factory that will create all values * during evaluation. + */ + public PartialEvaluator(ValueFactory valueFactory) + { + this(valueFactory, + new BasicInvocationUnit(valueFactory), + true); + } + + + /** + * Creates a new PartialEvaluator. + * @param valueFactory the value factory that will create all values + * during the evaluation. * @param invocationUnit the invocation unit that will handle all * communication with other fields and methods. * @param evaluateAllCode a flag that specifies whether all casts, branch @@ -109,6 +121,33 @@ public PartialEvaluator(ValueFactory valueFactory, this(valueFactory, invocationUnit, evaluateAllCode, + null); + } + + + /** + * Creates a new PartialEvaluator. + * @param valueFactory the value factory that will create all + * values during the 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 extraInstructionVisitor an optional extra visitor for all + * instructions right before they are + * executed. + */ + public PartialEvaluator(ValueFactory valueFactory, + InvocationUnit invocationUnit, + boolean evaluateAllCode, + InstructionVisitor extraInstructionVisitor) + { + this(valueFactory, + invocationUnit, + evaluateAllCode, + extraInstructionVisitor, evaluateAllCode ? new BasicBranchUnit() : new TracedBranchUnit(), @@ -126,6 +165,7 @@ private PartialEvaluator(PartialEvaluator partialEvaluator) this(partialEvaluator.valueFactory, partialEvaluator.invocationUnit, partialEvaluator.evaluateAllCode, + partialEvaluator.extraInstructionVisitor, partialEvaluator.branchUnit, partialEvaluator.branchTargetFinder, partialEvaluator.instructionBlockStack); @@ -154,15 +194,17 @@ private PartialEvaluator(PartialEvaluator partialEvaluator) private PartialEvaluator(ValueFactory valueFactory, InvocationUnit invocationUnit, boolean evaluateAllCode, + InstructionVisitor extraInstructionVisitor, BasicBranchUnit branchUnit, BranchTargetFinder branchTargetFinder, java.util.Stack callingInstructionBlockStack) { - this.valueFactory = valueFactory; - this.invocationUnit = invocationUnit; - this.evaluateAllCode = evaluateAllCode; - this.branchUnit = branchUnit; - this.branchTargetFinder = branchTargetFinder; + this.valueFactory = valueFactory; + this.invocationUnit = invocationUnit; + this.evaluateAllCode = evaluateAllCode; + this.extraInstructionVisitor = extraInstructionVisitor; + this.branchUnit = branchUnit; + this.branchTargetFinder = branchTargetFinder; this.callingInstructionBlockStack = callingInstructionBlockStack == null ? this.instructionBlockStack : callingInstructionBlockStack; @@ -219,11 +261,11 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt if (isTraced(offset)) { - int initializationOffset = branchTargetFinder.initializationOffset(offset); - if (initializationOffset != NONE) - { - System.out.println(" is to be initialized at ["+initializationOffset+"]"); - } +// int initializationOffset = branchTargetFinder.initializationOffset(offset); +// if (initializationOffset != NONE) +// { +// System.out.println(" is to be initialized at ["+initializationOffset+"]"); +// } InstructionOffsetValue branchTargets = branchTargets(offset); if (branchTargets != null) @@ -264,6 +306,10 @@ public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAt initializeArrays(codeAttribute); initializeParameters(clazz, method, codeAttribute, variables); + // Reset stacks. + instructionBlockStack.clear(); + callingInstructionBlockStack.clear(); + // Find all instruction offsets,... codeAttribute.accept(clazz, method, branchTargetFinder); @@ -299,11 +345,11 @@ public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAt if (isTraced(offset)) { - int initializationOffset = branchTargetFinder.initializationOffset(offset); - if (initializationOffset >= 0) - { - System.out.println(" is to be initialized at ["+initializationOffset+"]"); - } +// int initializationOffset = branchTargetFinder.initializationOffset(offset); +// if (initializationOffset != NONE) +// { +// System.out.println(" is to be initialized at ["+initializationOffset+"]"); +// } InstructionOffsetValue branchTargets = branchTargets(offset); if (branchTargets != null) @@ -358,6 +404,36 @@ public boolean isInstruction(int instructionOffset) } + /** + * Returns whether the instruction at the given offset is the target of + * any kind. + */ + public boolean isTarget(int instructionOffset) + { + return branchTargetFinder.isTarget(instructionOffset); + } + + + /** + * Returns whether the instruction at the given offset is the origin of a + * branch instruction. + */ + public boolean isBranchOrigin(int instructionOffset) + { + return branchTargetFinder.isBranchOrigin(instructionOffset); + } + + + /** + * Returns whether the instruction at the given offset is the target of a + * branch instruction. + */ + public boolean isBranchTarget(int instructionOffset) + { + return branchTargetFinder.isBranchTarget(instructionOffset); + } + + /** * Returns whether the instruction at the given offset is the target of a * branch instruction or an exception. @@ -369,6 +445,16 @@ public boolean isBranchOrExceptionTarget(int instructionOffset) } + /** + * Returns whether the instruction at the given offset is the start of + * an exception handler. + */ + public boolean isExceptionHandler(int instructionOffset) + { + return branchTargetFinder.isExceptionHandler(instructionOffset); + } + + /** * Returns whether the instruction at the given offset is the start of a * subroutine. @@ -419,47 +505,54 @@ public int subroutineEnd(int 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 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 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 instruction offset at which this initializer is calling - * the "super" or "this" initializer method, or NONE if it is - * not an initializer. + * Returns whether the instruction at the given offset creates a new, + * uninitialized instance. */ - public int superInitializationOffset() + public boolean isCreation(int offset) { - return branchTargetFinder.superInitializationOffset(); + return branchTargetFinder.isCreation(offset); } /** - * 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. + * Returns whether the instruction at the given offset is the special + * invocation of an instance initializer. */ - public int creationOffset(int offset) + public boolean isInitializer(int offset) { - return branchTargetFinder.creationOffset(offset); + return branchTargetFinder.isInitializer(offset); } @@ -523,6 +616,27 @@ public InstructionOffsetValue branchTargets(int instructionOffset) } + /** + * Returns a filtering version of the given instruction visitor that only + * visits traced instructions. + */ + public InstructionVisitor tracedInstructionFilter(InstructionVisitor instructionVisitor) + { + return tracedInstructionFilter(true, instructionVisitor); + } + + + /** + * Returns a filtering version of the given instruction visitor that only + * visits traced or untraced instructions. + */ + public InstructionVisitor tracedInstructionFilter(boolean traced, + InstructionVisitor instructionVisitor) + { + return new MyTracedInstructionFilter(traced, instructionVisitor); + } + + // Utility methods to evaluate instruction blocks. /** @@ -734,25 +848,27 @@ private void evaluateSingleInstructionBlock(Clazz clazz, variables.setProducerValue(storeValue); stack.setProducerValue(storeValue); - // Reset the trace value. - InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE; - - // Note that the instruction is only volatile. + // Decode the instruction. 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); + // Reset the branch unit. + branchUnit.reset(); if (DEBUG) { System.out.println(instruction.toString(instructionOffset)); } + if (extraInstructionVisitor != null) + { + // Visit the instruction with the optional visitor. + instruction.accept(clazz, + method, + codeAttribute, + instructionOffset, + extraInstructionVisitor); + } + try { // Process the instruction. The processor may modify the @@ -779,9 +895,6 @@ private void evaluateSingleInstructionBlock(Clazz clazz, InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets(); int branchTargetCount = branchTargets.instructionOffsetCount(); - // Stop tracing. - branchUnit.setTraceBranchTargets(traceValue); - if (DEBUG) { if (branchUnit.wasCalled()) @@ -828,7 +941,7 @@ private void evaluateSingleInstructionBlock(Clazz clazz, // Accumulate the branch targets at this offset. branchTargetValues[instructionOffset] = branchTargetValues[instructionOffset] == null ? branchTargets : - branchTargetValues[instructionOffset].generalize(branchTargets).instructionOffsetValue(); + branchTargetValues[instructionOffset].generalize(branchTargets); // Are there no branch targets at all? if (branchTargetCount == 0) @@ -844,7 +957,7 @@ private void evaluateSingleInstructionBlock(Clazz clazz, int branchTarget = branchTargets.instructionOffset(index); branchOriginValues[branchTarget] = branchOriginValues[branchTarget] == null ? instructionOffsetValue: - branchOriginValues[branchTarget].generalize(instructionOffsetValue).instructionOffsetValue(); + branchOriginValues[branchTarget].generalize(instructionOffsetValue); } // Are there multiple branch targets? @@ -864,10 +977,15 @@ private void evaluateSingleInstructionBlock(Clazz clazz, } if (DEBUG) System.out.println("Definite branch from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(0)+"]"); - } - // Just continue with the next instruction. - instructionOffset = branchTargets.instructionOffset(0); + // Continue at the definite branch target. + instructionOffset = branchTargets.instructionOffset(0); + } + else + { + // Just continue with the next instruction. + instructionOffset += instruction.length(instructionOffset); + } // Is this a subroutine invocation? if (instruction.opcode == InstructionConstants.OP_JSR || @@ -879,8 +997,7 @@ private void evaluateSingleInstructionBlock(Clazz clazz, codeAttribute, variables, stack, - instructionOffset, - instructionBlockStack); + instructionOffset); break; } @@ -908,8 +1025,7 @@ private void evaluateSubroutine(Clazz clazz, CodeAttribute codeAttribute, TracedVariables variables, TracedStack stack, - int subroutineStart, - java.util.Stack instructionBlockStack) + int subroutineStart) { int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart); @@ -956,7 +1072,7 @@ private void generalize(PartialEvaluator other, { branchOriginValues[offset] = branchOriginValues[offset] == null ? other.branchOriginValues[offset] : - branchOriginValues[offset].generalize(other.branchOriginValues[offset]).instructionOffsetValue(); + branchOriginValues[offset].generalize(other.branchOriginValues[offset]); } if (other.isTraced(offset)) @@ -965,7 +1081,7 @@ private void generalize(PartialEvaluator other, { branchTargetValues[offset] = branchTargetValues[offset] == null ? other.branchTargetValues[offset] : - branchTargetValues[offset].generalize(other.branchTargetValues[offset]).instructionOffsetValue(); + branchTargetValues[offset].generalize(other.branchTargetValues[offset]); } if (evaluationCounts[offset] == 0) @@ -1034,7 +1150,7 @@ public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAtt int endPC = exceptionInfo.u2endPC; // Do we have to evaluate this exception catch block? - if (isTraced(startPC, endPC)) + if (mayThrowExceptions(clazz, method, codeAttribute, startPC, endPC)) { int handlerPC = exceptionInfo.u2handlerPC; int catchType = exceptionInfo.u2catchType; @@ -1047,7 +1163,7 @@ public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAtt TracedStack stack = new TracedStack(codeAttribute.u2maxStack); // Initialize the trace values. - Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY); + Value storeValue = new InstructionOffsetValue(handlerPC | InstructionOffsetValue.EXCEPTION_HANDLER); variables.setProducerValue(storeValue); stack.setProducerValue(storeValue); @@ -1059,19 +1175,13 @@ public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAtt 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.NAME_JAVA_LANG_THROWABLE; - - Clazz catchClass = catchType != 0 ? - ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass : - null; - - stack.push(valueFactory.createReferenceValue(catchClassName, - catchClass, - false)); + // Initialize the stack. + invocationUnit.enterExceptionHandler(clazz, + method, + codeAttribute, + handlerPC, + catchType, + stack); int evaluationCount = evaluationCounts[handlerPC]; @@ -1194,12 +1304,15 @@ private void initializeParameters(Clazz clazz, CodeAttribute codeAttribute, TracedVariables variables) { - // Create the method parameters. - TracedVariables parameters = new TracedVariables(codeAttribute.u2maxLocals); +// // 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); - // Remember this instruction's offset with any stored value. - Value storeValue = new InstructionOffsetValue(AT_METHOD_ENTRY); - parameters.setProducerValue(storeValue); + // Create the method parameters. + Variables parameters = new Variables(codeAttribute.u2maxLocals); // Initialize the method parameters. invocationUnit.enterMethod(clazz, method, parameters); @@ -1212,13 +1325,39 @@ private void initializeParameters(Clazz clazz, // Initialize the variables with the parameters. variables.initialize(parameters); - // Set the store value of each parameter variable. - InstructionOffsetValue atMethodEntry = new InstructionOffsetValue(AT_METHOD_ENTRY); - + // Set the store value of each parameter variable. We store the + // variable indices of the parameters. These parameter offsets take + // into account Category 2 types. for (int index = 0; index < parameters.size(); index++) { - variables.setProducerValue(index, atMethodEntry); + InstructionOffsetValue producerValue = + new InstructionOffsetValue(index | InstructionOffsetValue.METHOD_PARAMETER); + + variables.setProducerValue(index, producerValue); + } + } + + + /** + * Returns whether a block of instructions may ever throw an exception. + */ + private boolean mayThrowExceptions(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int startOffset, + int endOffset) + { + for (int index = startOffset; index < endOffset; index++) + { + if (isTraced(index) && + (evaluateAllCode || + InstructionFactory.create(codeAttribute.code, index).mayThrowExceptions())) + { + return true; + } } + + return false; } @@ -1289,6 +1428,10 @@ private void generalizeVariables(int startOffset, } + /** + * This class represents an instruction block that has to be executed, + * starting with a given state at a given instruction offset. + */ private static class MyInstructionBlock { private TracedVariables variables; @@ -1305,4 +1448,85 @@ private MyInstructionBlock(TracedVariables variables, this.startOffset = startOffset; } } + + + /** + * This InstructionVisitor delegates its visits to a given + * InstructionVisitor, but only if the instruction has been traced (or not). + */ + private class MyTracedInstructionFilter implements InstructionVisitor + { + private final boolean traced; + private final InstructionVisitor instructionVisitor; + + + public MyTracedInstructionFilter(boolean traced, + InstructionVisitor instructionVisitor) + { + this.traced = traced; + this.instructionVisitor = instructionVisitor; + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitSimpleInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction); + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitTableSwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction); + } + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + if (shouldVisit(offset)) + { + instructionVisitor.visitLookUpSwitchInstruction(clazz, method, codeAttribute, offset, lookUpSwitchInstruction); + } + } + + + private boolean shouldVisit(int offset) + { + return isTraced(offset) == traced; + } + } } diff --git a/core/src/proguard/optimize/evaluation/ReferenceTracingInvocationUnit.java b/core/src/proguard/optimize/evaluation/ReferenceTracingInvocationUnit.java new file mode 100644 index 000000000..e3b6bf383 --- /dev/null +++ b/core/src/proguard/optimize/evaluation/ReferenceTracingInvocationUnit.java @@ -0,0 +1,211 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.CodeAttribute; +import proguard.classfile.constant.*; +import proguard.classfile.instruction.ConstantInstruction; +import proguard.evaluation.*; +import proguard.evaluation.value.*; + +/** + * This InvocationUnit tags reference values of retrieved fields, passed method + * parameters, method return values, and caught exceptions, so they can be + * traced throughout the execution of a method. The tags are instruction offsets + * or parameter indices (not parameter offsets). + * + * @see TracedReferenceValue + * @see InstructionOffsetValue + * @author Eric Lafortune + */ +public class ReferenceTracingInvocationUnit +extends SimplifiedInvocationUnit +{ + private final SimplifiedInvocationUnit invocationUnit; + + private int offset; + + + /** + * Creates a new ReferenceTracingInvocationUnit. + * @param invocationUnit the invocation unit to which invocations will + * be delegated. + */ + public ReferenceTracingInvocationUnit(SimplifiedInvocationUnit invocationUnit) + { + this.invocationUnit = invocationUnit; + } + + + // Implementations for InvocationUnit. + + public void enterExceptionHandler(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + int catchType, + Stack stack) + { + this.offset = offset; + + super.enterExceptionHandler(clazz, + method, + codeAttribute, + offset, + catchType, + stack); + } + + + public void invokeMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction, Stack stack) + { + this.offset = offset; + + super.invokeMember(clazz, + method, + codeAttribute, + offset, + constantInstruction, + stack); + } + + + // Implementations for SimplifiedInvocationUnit. + + public Value getExceptionValue(Clazz clazz, + ClassConstant catchClassConstant) + { + return trace(invocationUnit.getExceptionValue(clazz, catchClassConstant), + offset | InstructionOffsetValue.EXCEPTION_HANDLER); + } + + + public void setFieldClassValue(Clazz clazz, RefConstant refConstant, ReferenceValue value) + { + invocationUnit.setFieldClassValue(clazz, refConstant, value); + } + + + public Value getFieldClassValue(Clazz clazz, RefConstant refConstant, String type) + { + return trace(invocationUnit.getFieldClassValue(clazz, refConstant, type), + offset | InstructionOffsetValue.FIELD_VALUE); + } + + + public void setFieldValue(Clazz clazz, RefConstant refConstant, Value value) + { + invocationUnit.setFieldValue(clazz, refConstant, value); + } + + + public Value getFieldValue(Clazz clazz, RefConstant refConstant, String type) + { + return trace(invocationUnit.getFieldValue(clazz, refConstant, type), + offset | InstructionOffsetValue.FIELD_VALUE); + } + + + public void setMethodParameterValue(Clazz clazz, RefConstant refConstant, int parameterIndex, Value value) + { + invocationUnit.setMethodParameterValue(clazz, refConstant, parameterIndex, value); + } + + + public Value getMethodParameterValue(Clazz clazz, Method method, int parameterIndex, String type, Clazz referencedClass) + { + Value parameterValue = + invocationUnit.getMethodParameterValue(clazz, method, parameterIndex, type, referencedClass); + + // We're attaching the parameter index as a trace value. It doesn't + // take into account Category 2 values, so it is not compatible with + // variable indices. + return trace(parameterValue, + parameterIndex | InstructionOffsetValue.METHOD_PARAMETER); + } + + + public void setMethodReturnValue(Clazz clazz, Method method, Value value) + { + invocationUnit.setMethodReturnValue(clazz, method, value); + } + + + public Value getMethodReturnValue(Clazz clazz, RefConstant refConstant, String type) + { + Value returnValue = + invocationUnit.getMethodReturnValue(clazz, refConstant, type); + + return trace(returnValue, + offset | InstructionOffsetValue.METHOD_RETURN_VALUE); + } + + + public Value getMethodReturnValue(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant, String type) + { + Value returnValue = + invocationUnit.getMethodReturnValue(clazz, invokeDynamicConstant, type); + + return trace(returnValue, + offset | InstructionOffsetValue.METHOD_RETURN_VALUE); + } + + + // Small utility methods. + + /** + * Sets or replaces the trace value on a given value, if it's a + * reference value, returning the result. + */ + protected Value trace(Value value, int trace) + { + if (value.computationalType() != Value.TYPE_REFERENCE) + { + return value; + } + + return trace(value, new InstructionOffsetValue(trace)); + } + + + /** + * Sets or replaces the trace value on a given value, returning the result. + */ + protected Value trace(Value value, InstructionOffsetValue traceValue) + { + return new TracedReferenceValue(untrace(value).referenceValue(), + traceValue); + } + + + /** + * Removes the trace value from a given value, if present, returning the + * result. + */ + private Value untrace(Value value) + { + return value instanceof TracedReferenceValue ? + ((TracedReferenceValue)value).getReferenceValue() : + value; + } +} \ No newline at end of file diff --git a/core/src/proguard/optimize/evaluation/ReferenceTracingValueFactory.java b/core/src/proguard/optimize/evaluation/ReferenceTracingValueFactory.java new file mode 100644 index 000000000..ad1adb38e --- /dev/null +++ b/core/src/proguard/optimize/evaluation/ReferenceTracingValueFactory.java @@ -0,0 +1,301 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.CodeAttribute; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.evaluation.value.*; + +/** + * This ValueFactory tags newly created reference values so they can be traced + * throughout the execution of a method. + * + * @see TracedReferenceValue + * @see InstructionOffsetValue + * @author Eric Lafortune + */ +public class ReferenceTracingValueFactory +extends SimplifiedVisitor +implements InstructionVisitor, + ValueFactory +{ + private final ValueFactory valueFactory; + private final boolean preserveTraceValueOnCasts; + + private Value traceValue; + + + /** + * Creates a new ReferenceTracingValueFactory that attaches instruction + * offset values based on being used as an instruction visitor. This + * instance preserves trace values in the {@link #cast} method. + * @param valueFactory the value factory that creates the actual values. + */ + public ReferenceTracingValueFactory(ValueFactory valueFactory) + { + this(valueFactory, true); + } + + + /** + * Creates a new ReferenceTracingValueFactory that attaches instruction + * offset values based on being used as an instruction visitor. + * @param valueFactory the value factory that creates the + * actual values. + * @param preserveTraceValueOnCasts specifies whether to preserve the + * trace value for reference values that + * are passed to the {@link #cast} method. + */ + public ReferenceTracingValueFactory(ValueFactory valueFactory, + boolean preserveTraceValueOnCasts) + { + this.valueFactory = valueFactory; + this.preserveTraceValueOnCasts = preserveTraceValueOnCasts; + } + + + public void setTraceValue(Value traceValue) + { + this.traceValue = traceValue; + } + + + /** + * Casts a given traced reference value to the given type, either keeping + * its trace value or setting a new one. + */ + public TracedReferenceValue cast(TracedReferenceValue referenceValue, + String type, + Clazz referencedClass, + boolean alwaysCast) + { + // Cast the value. + ReferenceValue castValue = + referenceValue.getReferenceValue().cast(type, + referencedClass, + valueFactory, + alwaysCast); + + // Trace it. + return new TracedReferenceValue(castValue, + preserveTraceValueOnCasts ? + referenceValue.getTraceValue() : + traceValue); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + traceValue = null; + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + case InstructionConstants.OP_ACONST_NULL: + case InstructionConstants.OP_NEWARRAY: + case InstructionConstants.OP_ATHROW: + traceValue = new InstructionOffsetValue(offset | InstructionOffsetValue.NEW_INSTANCE); + break; + + case InstructionConstants.OP_AALOAD: + traceValue = new InstructionOffsetValue(offset); + break; + + default: + traceValue = null; + break; + } + } + + + 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_NEW: + case InstructionConstants.OP_ANEWARRAY: + case InstructionConstants.OP_MULTIANEWARRAY: + traceValue = new InstructionOffsetValue(offset | InstructionOffsetValue.NEW_INSTANCE); + break; + + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_GETFIELD: + traceValue = new InstructionOffsetValue(offset | InstructionOffsetValue.FIELD_VALUE); + break; + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + traceValue = new InstructionOffsetValue(offset | InstructionOffsetValue.METHOD_RETURN_VALUE); + break; + + case InstructionConstants.OP_CHECKCAST: + traceValue = new InstructionOffsetValue(offset | InstructionOffsetValue.CAST); + break; + + default: + traceValue = null; + break; + } + } + + + // Implementations for BasicValueFactory. + + public Value createValue(String type, + Clazz referencedClass, + boolean mayBeExtension, + boolean mayBeNull) + { + return trace(valueFactory.createValue(type, + referencedClass, + mayBeExtension, + mayBeNull)); + } + + + public IntegerValue createIntegerValue() + { + return valueFactory.createIntegerValue(); + } + + + public IntegerValue createIntegerValue(int value) + { + return valueFactory.createIntegerValue(value); + } + + + public LongValue createLongValue() + { + return valueFactory.createLongValue(); + } + + + public LongValue createLongValue(long value) + { + return valueFactory.createLongValue(value); + } + + + public FloatValue createFloatValue() + { + return valueFactory.createFloatValue(); + } + + + public FloatValue createFloatValue(float value) + { + return valueFactory.createFloatValue(value); + } + + + public DoubleValue createDoubleValue() + { + return valueFactory.createDoubleValue(); + } + + + public DoubleValue createDoubleValue(double value) + { + return valueFactory.createDoubleValue(value); + } + + + public ReferenceValue createReferenceValue() + { + return trace(valueFactory.createReferenceValue()); + } + + + public ReferenceValue createReferenceValueNull() + { + return trace(valueFactory.createReferenceValueNull()); + } + + + public ReferenceValue createReferenceValue(String type, + Clazz referencedClass, + boolean mayBeExtension, + boolean mayBeNull) + { + return trace(valueFactory.createReferenceValue(type, + referencedClass, + mayBeExtension, + mayBeNull)); + } + + + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength) + { + return trace(valueFactory.createArrayReferenceValue(type, referencedClass, arrayLength)); + } + + + /** + * Creates a new ReferenceValue that represents an array with elements of + * the given type, with the given length and initial element values. + */ + public ReferenceValue createArrayReferenceValue(String type, + Clazz referencedClass, + IntegerValue arrayLength, + Value elementValue) + { + return trace(valueFactory.createArrayReferenceValue(type, referencedClass, arrayLength, elementValue)); + } + + + // Small utility methods. + + /** + * Attaches the current trace value to given value, if it is a reference + * value. + */ + public Value trace(Value value) + { + return value.computationalType() == Value.TYPE_REFERENCE ? + trace(value.referenceValue()) : + value; + } + + /** + * Attaches the current trace value to given reference value. + */ + public ReferenceValue trace(ReferenceValue referenceValue) + { + return traceValue != null ? + new TracedReferenceValue(referenceValue, traceValue) : + referenceValue; + } +} diff --git a/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java b/core/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java similarity index 54% rename from src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java rename to core/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java index 73b8b8ff9..552bee967 100644 --- a/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java +++ b/core/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,6 +24,7 @@ import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.*; import proguard.evaluation.value.*; +import proguard.optimize.OptimizationInfoMemberFilter; import proguard.optimize.info.*; /** @@ -38,7 +39,17 @@ public class SimpleEnumArrayPropagator implements ClassVisitor, MemberVisitor { - private final ValueFactory valueFactory = new ParticularValueFactory(); + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("enum") != null; + //*/ + + + private final MemberVisitor fieldArrayFinder = new MemberDescriptorFilter("[I", this); + private final MemberVisitor methodArrayPropagator = new OptimizationInfoMemberFilter( + new MemberDescriptorFilter("()[I", this)); + private final ValueFactory valueFactory = new ParticularValueFactory(); private Value array; @@ -47,48 +58,49 @@ public class SimpleEnumArrayPropagator public void visitProgramClass(ProgramClass programClass) { - // Update the return value of the "int[] values()" method. - programClass.methodsAccept(new MemberDescriptorFilter("()[I", this)); - } - - - // Implementations for MemberVisitor. + // Find the array of the "int[] $VALUES" field. + array = null; - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) - { - // Find the array length of the "int[] $VALUES" field. - programClass.fieldsAccept(new MemberDescriptorFilter("[I", this)); + programClass.fieldsAccept(fieldArrayFinder); if (array != null) { - // Set the array value with the found array length. We can't use - // the original array, because its elements might get overwritten. - Value propagatedArray = - valueFactory.createArrayReferenceValue("I", - null, - array.referenceValue().arrayLength( - valueFactory)); - - setMethodReturnValue(programMethod, propagatedArray); - - array = null; + // Update the return value of the "int[] values()" method. + programClass.methodsAccept(methodArrayPropagator); } } + + // Implementations for MemberVisitor. + public void visitProgramField(ProgramClass programClass, ProgramField programField) { array = StoringInvocationUnit.getFieldValue(programField); } + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Set the array value with the found array length. We can't use + // the original array, because its elements might get overwritten. + Value propagatedArray = + valueFactory.createArrayReferenceValue("I", + null, + array.referenceValue().arrayLength(valueFactory)); + + if (DEBUG) + { + System.out.println("SimpleEnumArrayPropagator: ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]: propagating ["+propagatedArray+"] as return value"); + } + + setMethodReturnValue(programMethod, propagatedArray); + } + + // Small utility methods. private static void setMethodReturnValue(Method method, Value value) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setReturnValue(value); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setReturnValue( value); } } diff --git a/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java b/core/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java similarity index 79% rename from src/proguard/optimize/evaluation/SimpleEnumClassChecker.java rename to core/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java index 9208402d9..af6f2911c 100644 --- a/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java +++ b/core/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,6 +22,7 @@ import proguard.classfile.*; import proguard.classfile.visitor.*; +import proguard.optimize.OptimizationInfoClassFilter; import proguard.optimize.info.SimpleEnumMarker; /** @@ -40,12 +41,13 @@ public class SimpleEnumClassChecker //*/ - private final SimpleEnumMarker simpleEnumMarker = new SimpleEnumMarker(true); - private final MemberVisitor virtualMemberChecker = new MemberAccessFilter(0, - ClassConstants.ACC_PRIVATE | - ClassConstants.ACC_STATIC, - new MemberToClassVisitor( - new SimpleEnumMarker(false))); + private final ClassVisitor simpleEnumMarker = new OptimizationInfoClassFilter( + new SimpleEnumMarker(true)); + private final MemberVisitor virtualMemberChecker = new MemberAccessFilter(0, + ClassConstants.ACC_PRIVATE | + ClassConstants.ACC_STATIC, + new MemberToClassVisitor( + new SimpleEnumMarker(false))); // Implementations for ClassVisitor. @@ -72,4 +74,4 @@ public void visitProgramClass(ProgramClass programClass) programClass.methodsAccept(virtualMemberChecker); } } -} +} \ No newline at end of file diff --git a/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java b/core/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java similarity index 71% rename from src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java rename to core/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java index 07a17680f..caea0fe6c 100644 --- a/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java +++ b/core/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -88,30 +88,49 @@ public class SimpleEnumClassSimplifier new Utf8Constant(ClassConstants.METHOD_TYPE_INIT_ENUM), }; - private static final Instruction[] INSTRUCTIONS = new Instruction[] + private static final Instruction[][][] INSTRUCTION_SEQUENCES = new Instruction[][][] { - new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_ENUM), - new SimpleInstruction(InstructionConstants.OP_DUP), - new ConstantInstruction(InstructionConstants.OP_LDC, STRING_ENUM_CONSTANT_NAME), - new SimpleInstruction(InstructionConstants.OP_ICONST_0, ENUM_CONSTANT_ORDINAL), - new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_ENUM_INIT), - }; - - private static final Instruction[] REPLACEMENT_INSTRUCTIONS = new Instruction[] - { - new SimpleInstruction(InstructionConstants.OP_SIPUSH, ENUM_CONSTANT_ORDINAL), - new SimpleInstruction(InstructionConstants.OP_ICONST_1), - new SimpleInstruction(InstructionConstants.OP_IADD), + { + // Replace new Enum("name", constant) + // by constant + 1. + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_ENUM), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_ENUM_CONSTANT_NAME), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, ENUM_CONSTANT_ORDINAL), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_ENUM_INIT), + }, + { + new SimpleInstruction(InstructionConstants.OP_SIPUSH, ENUM_CONSTANT_ORDINAL), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new SimpleInstruction(InstructionConstants.OP_IADD), + } + }, + { + // The name constants may have been encrypted. + // Replace (..., constant) + // by (..., 0); pop; constant + 1. + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0, ENUM_CONSTANT_ORDINAL), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_ENUM_INIT), + }, + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_ENUM_INIT), + new SimpleInstruction(InstructionConstants.OP_POP), + new SimpleInstruction(InstructionConstants.OP_SIPUSH, ENUM_CONSTANT_ORDINAL), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new SimpleInstruction(InstructionConstants.OP_IADD), + } + }, }; - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true); - private final InstructionSequenceReplacer instructionSequenceReplacer = - new InstructionSequenceReplacer(CONSTANTS, - INSTRUCTIONS, - REPLACEMENT_INSTRUCTIONS, + private final InstructionSequencesReplacer instructionSequenceReplacer = + new InstructionSequencesReplacer(CONSTANTS, + INSTRUCTION_SEQUENCES, null, codeAttributeEditor); diff --git a/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java b/core/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java similarity index 98% rename from src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java rename to core/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java index 1dcd80e37..85ffd0b33 100644 --- a/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java +++ b/core/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,9 +26,9 @@ import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.editor.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; import proguard.classfile.visitor.*; +import proguard.optimize.KeepMarker; import proguard.optimize.info.*; /** @@ -217,11 +217,9 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie programField.u2accessFlags &= ~ClassConstants.ACC_ENUM; // Clear the field value. - FieldOptimizationInfo fieldOptimizationInfo = - FieldOptimizationInfo.getFieldOptimizationInfo(programField); - if (fieldOptimizationInfo != null) + if (!KeepMarker.isKept(programField)) { - fieldOptimizationInfo.resetValue(programClass, programField); + ProgramFieldOptimizationInfo.getProgramFieldOptimizationInfo(programField).resetValue(programClass, programField); } // Simplify the signature. diff --git a/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java b/core/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java similarity index 97% rename from src/proguard/optimize/evaluation/SimpleEnumUseChecker.java rename to core/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java index 37c885af7..bb06134f7 100644 --- a/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java +++ b/core/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -31,6 +31,7 @@ import proguard.classfile.visitor.*; import proguard.evaluation.*; import proguard.evaluation.value.*; +import proguard.optimize.OptimizationInfoClassFilter; import proguard.optimize.info.SimpleEnumMarker; /** @@ -58,8 +59,8 @@ public class SimpleEnumUseChecker private final PartialEvaluator partialEvaluator; private final MemberVisitor methodCodeChecker = new AllAttributeVisitor(this); private final ConstantVisitor invokedMethodChecker = new ReferencedMemberVisitor(this); - private final ConstantVisitor parameterChecker = new ReferencedMemberVisitor(new AllParameterVisitor(this)); - private final ClassVisitor complexEnumMarker = new SimpleEnumMarker(false); + private final ConstantVisitor parameterChecker = new ReferencedMemberVisitor(new AllParameterVisitor(false, this)); + private final ClassVisitor complexEnumMarker = new OptimizationInfoClassFilter(new SimpleEnumMarker(false)); private final ReferencedClassVisitor referencedComplexEnumMarker = new ReferencedClassVisitor(complexEnumMarker); @@ -72,7 +73,7 @@ public class SimpleEnumUseChecker */ public SimpleEnumUseChecker() { - this(new PartialEvaluator()); + this(new PartialEvaluator(new TypedReferenceValueFactory())); } @@ -91,7 +92,7 @@ public SimpleEnumUseChecker(PartialEvaluator partialEvaluator) public void visitProgramClass(ProgramClass programClass) { - if ((programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) != 0) + if ((programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATION) != 0) { // Unmark the simple enum classes in annotations. programClass.methodsAccept(referencedComplexEnumMarker); @@ -475,11 +476,11 @@ private void checkMixedStackEntriesBefore(int offset) // Check all producers. for (int producerIndex = 0; producerIndex < producerCount; producerIndex++) { - int producerOffset = - producerOffsets.instructionOffset(producerIndex); - - if (producerOffset >= 0) + if (!producerOffsets.isExceptionHandler(producerIndex)) { + int producerOffset = + producerOffsets.instructionOffset(producerIndex); + if (DEBUG) { ReferenceValue producedValue = @@ -537,11 +538,11 @@ private void checkMixedVariablesBefore(int offset) // Check all producers. for (int producerIndex = 0; producerIndex < producerCount; producerIndex++) { - int producerOffset = - producerOffsets.instructionOffset(producerIndex); - - if (producerOffset >= 0) + if (!producerOffsets.isMethodParameter(producerIndex)) { + int producerOffset = + producerOffsets.instructionOffset(producerIndex); + if (DEBUG) { ReferenceValue producedValue = diff --git a/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java b/core/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java similarity index 96% rename from src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java rename to core/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java index 295cdfbc8..340faf308 100644 --- a/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java +++ b/core/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -54,11 +54,12 @@ public class SimpleEnumUseSimplifier private static boolean DEBUG = System.getProperty("enum") != null; //*/ + private final InstructionVisitor extraInstructionVisitor; private final PartialEvaluator partialEvaluator; private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true); - private final ConstantVisitor nullParameterFixer = new ReferencedMemberVisitor(new AllParameterVisitor(this)); + private final ConstantVisitor nullParameterFixer = new ReferencedMemberVisitor(new AllParameterVisitor(false, this)); // Fields acting as parameters and return values for the visitor methods. private Clazz invocationClazz; @@ -73,7 +74,7 @@ public class SimpleEnumUseSimplifier */ public SimpleEnumUseSimplifier() { - this(new PartialEvaluator(), null); + this(new PartialEvaluator(new TypedReferenceValueFactory()), null); } @@ -799,19 +800,22 @@ private void replaceNullVariableProducers(Clazz clazz, for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++) { - int producerOffset = producerOffsets.instructionOffset(index); - - if (producerOffset >= 0 && - partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue().isNull() == Value.ALWAYS) + if (!producerOffsets.isMethodParameter(index) && + !producerOffsets.isExceptionHandler(index)) { - // Replace loading null by loading 0. - replaceInstruction(clazz, - producerOffset, - new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex), - new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex)); + int producerOffset = producerOffsets.instructionOffset(index); - // Replace pushing null by pushing 0. - replaceNullStackEntryProducers(clazz, method, codeAttribute, producerOffset); + if (partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue().isNull() == Value.ALWAYS) + { + // Replace loading null by loading 0. + replaceInstruction(clazz, + producerOffset, + new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex), + new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex)); + + // Replace pushing null by pushing 0. + replaceNullStackEntryProducers(clazz, method, codeAttribute, producerOffset); + } } } } diff --git a/src/proguard/optimize/evaluation/StoringInvocationUnit.java b/core/src/proguard/optimize/evaluation/StoringInvocationUnit.java similarity index 65% rename from src/proguard/optimize/evaluation/StoringInvocationUnit.java rename to core/src/proguard/optimize/evaluation/StoringInvocationUnit.java index 27af58cc3..6de19c81b 100644 --- a/src/proguard/optimize/evaluation/StoringInvocationUnit.java +++ b/core/src/proguard/optimize/evaluation/StoringInvocationUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,6 +24,7 @@ import proguard.classfile.constant.RefConstant; import proguard.evaluation.BasicInvocationUnit; import proguard.evaluation.value.*; +import proguard.optimize.KeepMarker; import proguard.optimize.info.*; /** @@ -69,9 +70,9 @@ public StoringInvocationUnit(ValueFactory valueFactory, // Implementations for BasicInvocationUnit. - protected void setFieldClassValue(Clazz clazz, - RefConstant refConstant, - ReferenceValue value) + public void setFieldClassValue(Clazz clazz, + RefConstant refConstant, + ReferenceValue value) { if (storeFieldValues) { @@ -84,9 +85,9 @@ protected void setFieldClassValue(Clazz clazz, } - protected void setFieldValue(Clazz clazz, - RefConstant refConstant, - Value value) + public void setFieldValue(Clazz clazz, + RefConstant refConstant, + Value value) { if (storeFieldValues) { @@ -99,10 +100,10 @@ protected void setFieldValue(Clazz clazz, } - protected void setMethodParameterValue(Clazz clazz, - RefConstant refConstant, - int parameterIndex, - Value value) + public void setMethodParameterValue(Clazz clazz, + RefConstant refConstant, + int parameterIndex, + Value value) { if (storeMethodParameterValues) { @@ -117,9 +118,9 @@ protected void setMethodParameterValue(Clazz clazz, } - protected void setMethodReturnValue(Clazz clazz, - Method method, - Value value) + public void setMethodReturnValue(Clazz clazz, + Method method, + Value value) { if (storeMethodReturnValues) { @@ -132,76 +133,60 @@ protected void setMethodReturnValue(Clazz clazz, private static void generalizeFieldClassValue(Field field, ReferenceValue value) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - if (info != null) + if (!KeepMarker.isKept(field)) { - info.generalizeReferencedClass(value); + ProgramFieldOptimizationInfo.getProgramFieldOptimizationInfo(field).generalizeReferencedClass(value); } } public static ReferenceValue getFieldClassValue(Field field) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - return info != null ? - info.getReferencedClass() : - null; + return FieldOptimizationInfo.getFieldOptimizationInfo(field).getReferencedClass(); } private static void generalizeFieldValue(Field field, Value value) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - if (info != null) + if (!KeepMarker.isKept(field)) { - info.generalizeValue(value); + ProgramFieldOptimizationInfo.getProgramFieldOptimizationInfo(field).generalizeValue(value); } } public static Value getFieldValue(Field field) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - return info != null ? - info.getValue() : - null; + return FieldOptimizationInfo.getFieldOptimizationInfo(field).getValue(); } private static void generalizeMethodParameterValue(Method method, int parameterIndex, Value value) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) + if (!KeepMarker.isKept(method)) { - info.generalizeParameter(parameterIndex, value); + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).generalizeParameterValue(parameterIndex, value); } } public static Value getMethodParameterValue(Method method, int parameterIndex) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info != null ? - info.getParameter(parameterIndex) : - null; + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getParameterValue(parameterIndex); } private static void generalizeMethodReturnValue(Method method, Value value) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) + if (!KeepMarker.isKept(method)) { - info.generalizeReturnValue(value); + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).generalizeReturnValue(value); } } public static Value getMethodReturnValue(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info != null ? - info.getReturnValue() : - null; + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getReturnValue(); } } diff --git a/src/proguard/optimize/evaluation/TracedBranchUnit.java b/core/src/proguard/optimize/evaluation/TracedBranchUnit.java similarity index 69% rename from src/proguard/optimize/evaluation/TracedBranchUnit.java rename to core/src/proguard/optimize/evaluation/TracedBranchUnit.java index 8d6e1504a..019897dcb 100644 --- a/src/proguard/optimize/evaluation/TracedBranchUnit.java +++ b/core/src/proguard/optimize/evaluation/TracedBranchUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -33,8 +33,32 @@ class TracedBranchUnit extends BasicBranchUnit { + private boolean isFixed; + + + // Implementations for BasicBranchUnit. + + public void reset() + { + super.reset(); + + isFixed = false; + } + + // Implementations for BranchUnit. + public void branch(Clazz clazz, + CodeAttribute codeAttribute, + int offset, + int branchTarget) + { + super.branch(clazz, codeAttribute, offset, branchTarget); + + isFixed = true; + } + + public void branchConditionally(Clazz clazz, CodeAttribute codeAttribute, int offset, @@ -45,15 +69,20 @@ public void branchConditionally(Clazz clazz, { // Always branch. super.branch(clazz, codeAttribute, offset, branchTarget); + + isFixed = true; } - else if (conditional != Value.NEVER) + else if (conditional == Value.MAYBE) { - // Maybe branch. - super.branchConditionally(clazz, codeAttribute, offset, branchTarget, conditional); + if (!isFixed) + { + // Maybe branch. + super.branchConditionally(clazz, codeAttribute, offset, branchTarget, conditional); + } } else { - super.setCalled(); + super.wasCalled = true; } } } diff --git a/src/proguard/optimize/evaluation/VariableOptimizer.java b/core/src/proguard/optimize/evaluation/VariableOptimizer.java similarity index 98% rename from src/proguard/optimize/evaluation/VariableOptimizer.java rename to core/src/proguard/optimize/evaluation/VariableOptimizer.java index 239ec2ea6..9b709f929 100644 --- a/src/proguard/optimize/evaluation/VariableOptimizer.java +++ b/core/src/proguard/optimize/evaluation/VariableOptimizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -42,7 +42,7 @@ public class VariableOptimizer //* private static final boolean DEBUG = false; /*/ - private static boolean DEBUG = System.getProperty("vo") != null; + private static boolean DEBUG = true; //*/ private static final int MAX_VARIABLES_SIZE = 64; diff --git a/src/proguard/optimize/evaluation/package.html b/core/src/proguard/optimize/evaluation/package.html similarity index 100% rename from src/proguard/optimize/evaluation/package.html rename to core/src/proguard/optimize/evaluation/package.html diff --git a/src/proguard/optimize/info/AccessMethodMarker.java b/core/src/proguard/optimize/info/AccessMethodMarker.java similarity index 81% rename from src/proguard/optimize/info/AccessMethodMarker.java rename to core/src/proguard/optimize/info/AccessMethodMarker.java index b030c55dd..1b03afc61 100644 --- a/src/proguard/optimize/info/AccessMethodMarker.java +++ b/core/src/proguard/optimize/info/AccessMethodMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -65,7 +65,7 @@ public void visitAnyConstant(Clazz clazz, Constant constant) {} public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { - // Check the referenced class or class member, if any. + // Check the referenced class or class member, if any. stringConstant.referencedClassAccept(this); stringConstant.referencedMemberAccept(this); } @@ -141,11 +141,7 @@ else if ((accessFlags & ClassConstants.ACC_PUBLIC) == 0) private static void setAccessesPrivateCode(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setAccessesPrivateCode(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setAccessesPrivateCode(); } @@ -154,18 +150,13 @@ private static void setAccessesPrivateCode(Method method) */ public static boolean accessesPrivateCode(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || info.accessesPrivateCode(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).accessesPrivateCode(); } private static void setAccessesPackageCode(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setAccessesPackageCode(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setAccessesPackageCode(); } @@ -175,18 +166,13 @@ private static void setAccessesPackageCode(Method method) */ public static boolean accessesPackageCode(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || info.accessesPackageCode(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).accessesPackageCode(); } private static void setAccessesProtectedCode(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setAccessesProtectedCode(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setAccessesProtectedCode(); } @@ -195,7 +181,6 @@ private static void setAccessesProtectedCode(Method method) */ public static boolean accessesProtectedCode(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || info.accessesProtectedCode(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).accessesProtectedCode(); } } diff --git a/src/proguard/optimize/info/BackwardBranchMarker.java b/core/src/proguard/optimize/info/BackwardBranchMarker.java similarity index 86% rename from src/proguard/optimize/info/BackwardBranchMarker.java rename to core/src/proguard/optimize/info/BackwardBranchMarker.java index af1862e36..3da8806fd 100644 --- a/src/proguard/optimize/info/BackwardBranchMarker.java +++ b/core/src/proguard/optimize/info/BackwardBranchMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -74,17 +74,12 @@ private void markBackwardBranch(Method method, int branchOffset) private static void setBranchesBackward(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setBranchesBackward(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setBranchesBackward(); } public static boolean branchesBackward(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || info.branchesBackward(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).branchesBackward(); } } diff --git a/src/proguard/optimize/info/CatchExceptionMarker.java b/core/src/proguard/optimize/info/CatchExceptionMarker.java similarity index 81% rename from src/proguard/optimize/info/CatchExceptionMarker.java rename to core/src/proguard/optimize/info/CatchExceptionMarker.java index 9105142d6..213963f9e 100644 --- a/src/proguard/optimize/info/CatchExceptionMarker.java +++ b/core/src/proguard/optimize/info/CatchExceptionMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -52,18 +52,12 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt private static void markCatchException(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setCatchesExceptions(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setCatchesExceptions(); } public static boolean catchesExceptions(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || - info.catchesExceptions(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).catchesExceptions(); } } diff --git a/src/proguard/optimize/info/CaughtClassFilter.java b/core/src/proguard/optimize/info/CaughtClassFilter.java similarity index 97% rename from src/proguard/optimize/info/CaughtClassFilter.java rename to core/src/proguard/optimize/info/CaughtClassFilter.java index 78f10cf03..1a63c50f8 100644 --- a/src/proguard/optimize/info/CaughtClassFilter.java +++ b/core/src/proguard/optimize/info/CaughtClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/CaughtClassMarker.java b/core/src/proguard/optimize/info/CaughtClassMarker.java similarity index 81% rename from src/proguard/optimize/info/CaughtClassMarker.java rename to core/src/proguard/optimize/info/CaughtClassMarker.java index 9d4a1e9e5..a124aaf45 100644 --- a/src/proguard/optimize/info/CaughtClassMarker.java +++ b/core/src/proguard/optimize/info/CaughtClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -47,17 +47,12 @@ public void visitProgramClass(ProgramClass programClass) private static void setCaught(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setCaught(); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setCaught(); } public static boolean isCaught(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - return info == null || info.isCaught(); + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).isCaught(); } } \ No newline at end of file diff --git a/core/src/proguard/optimize/info/ClassOptimizationInfo.java b/core/src/proguard/optimize/info/ClassOptimizationInfo.java new file mode 100644 index 000000000..4789bc696 --- /dev/null +++ b/core/src/proguard/optimize/info/ClassOptimizationInfo.java @@ -0,0 +1,144 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 +{ + protected boolean hasNoSideEffects = false; + + + public void setNoSideEffects() + { + hasNoSideEffects = true; + } + + + public boolean hasNoSideEffects() + { + return hasNoSideEffects; + } + + + public boolean isKept() + { + return true; + } + + + public boolean isInstantiated() + { + return true; + } + + + public boolean isInstanceofed() + { + // We're relaxing the strict assumption of "true". + return !hasNoSideEffects; + } + + + public boolean isDotClassed() + { + // We're relaxing the strict assumption of "true". + return !hasNoSideEffects; + } + + + public boolean isCaught() + { + return true; + } + + + public boolean isSimpleEnum() + { + return false; + } + + + public boolean isWrapper() + { + return false; + } + + + public boolean isEscaping() + { + return true; + } + + + public boolean hasSideEffects() + { + return !hasNoSideEffects; + } + + + public boolean containsPackageVisibleMembers() + { + return true; + } + + + public boolean invokesPackageVisibleMembers() + { + return true; + } + + + public boolean mayBeMerged() + { + return false; + } + + + public Clazz getWrappedClass() + { + return null; + } + + + public Clazz getTargetClass() + { + return null; + } + + + public static void setClassOptimizationInfo(Clazz clazz) + { + clazz.setVisitorInfo(new ClassOptimizationInfo()); + } + + + public static ClassOptimizationInfo getClassOptimizationInfo(Clazz clazz) + { + return (ClassOptimizationInfo)clazz.getVisitorInfo(); + } +} diff --git a/core/src/proguard/optimize/info/CodeAttributeOptimizationInfo.java b/core/src/proguard/optimize/info/CodeAttributeOptimizationInfo.java new file mode 100644 index 000000000..aa41d358d --- /dev/null +++ b/core/src/proguard/optimize/info/CodeAttributeOptimizationInfo.java @@ -0,0 +1,51 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.attribute.CodeAttribute; + +/** + * This class stores some optimization information that can be attached + * to a code attribute. + * + * @author Thomas Neidhart + */ +public class CodeAttributeOptimizationInfo +{ + + public boolean isKept() + { + return true; + } + + + public static void setCodeAttributeOptimizationInfo(CodeAttribute codeAttribute) + { + codeAttribute.setVisitorInfo(new CodeAttributeOptimizationInfo()); + } + + + public static CodeAttributeOptimizationInfo getCodeAttributeOptimizationInfo(CodeAttribute codeAttribute) + { + return (CodeAttributeOptimizationInfo)codeAttribute.getVisitorInfo(); + } + +} diff --git a/src/proguard/optimize/info/DotClassFilter.java b/core/src/proguard/optimize/info/DotClassFilter.java similarity index 97% rename from src/proguard/optimize/info/DotClassFilter.java rename to core/src/proguard/optimize/info/DotClassFilter.java index 9c8556835..739ba5779 100644 --- a/src/proguard/optimize/info/DotClassFilter.java +++ b/core/src/proguard/optimize/info/DotClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/DotClassMarker.java b/core/src/proguard/optimize/info/DotClassMarker.java similarity index 81% rename from src/proguard/optimize/info/DotClassMarker.java rename to core/src/proguard/optimize/info/DotClassMarker.java index d7f8fa314..0989a9276 100644 --- a/src/proguard/optimize/info/DotClassMarker.java +++ b/core/src/proguard/optimize/info/DotClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,6 +28,7 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; +import proguard.optimize.OptimizationInfoClassFilter; /** * This InstructionVisitor marks all classes that are used in a .class @@ -41,6 +42,9 @@ public class DotClassMarker ConstantVisitor, ClassVisitor { + private final OptimizationInfoClassFilter filteredClassMarker = new OptimizationInfoClassFilter(this); + + // Implementations for InstructionVisitor. public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} @@ -62,17 +66,15 @@ public void visitAnyConstant(Clazz clazz, Constant constant) {} public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { - classConstant.referencedClassAccept(this); + classConstant.referencedClassAccept(filteredClassMarker); } // Implementations for ClassVisitor. - public void visitLibraryClass(LibraryClass libraryClass) {} - - public void visitProgramClass(ProgramClass programClass) + public void visitAnyClass(Clazz clazz) { - setDotClassed(programClass); + setDotClassed(clazz); } @@ -80,17 +82,12 @@ public void visitProgramClass(ProgramClass programClass) private static void setDotClassed(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setDotClassed(); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setDotClassed(); } public static boolean isDotClassed(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - return info == null || info.isDotClassed(); + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).isDotClassed(); } } \ No newline at end of file diff --git a/src/proguard/optimize/info/DynamicInvocationMarker.java b/core/src/proguard/optimize/info/DynamicInvocationMarker.java similarity index 84% rename from src/proguard/optimize/info/DynamicInvocationMarker.java rename to core/src/proguard/optimize/info/DynamicInvocationMarker.java index f59244cfb..0b50a3b7c 100644 --- a/src/proguard/optimize/info/DynamicInvocationMarker.java +++ b/core/src/proguard/optimize/info/DynamicInvocationMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,7 +22,6 @@ 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; @@ -60,11 +59,7 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c private static void setInvokesDynamically(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setInvokesDynamically(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setInvokesDynamically(); } @@ -73,7 +68,6 @@ private static void setInvokesDynamically(Method method) */ public static boolean invokesDynamically(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || info.invokesDynamically(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).invokesDynamically(); } } diff --git a/core/src/proguard/optimize/info/EscapingClassFilter.java b/core/src/proguard/optimize/info/EscapingClassFilter.java new file mode 100644 index 000000000..0c5141945 --- /dev/null +++ b/core/src/proguard/optimize/info/EscapingClassFilter.java @@ -0,0 +1,99 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 its visits to one of two other given + * ClassVisitor instances, depending on whether the classes are marked to be + * escaping or not. + * + * @see EscapingClassMarker + * + * @author Eric Lafortune + */ +public class EscapingClassFilter +implements ClassVisitor +{ + private final ClassVisitor escapingClassVisitor; + private final ClassVisitor otherClassVisitor; + + + /** + * Creates a new EscapingClassFilter. + * @param escapingClassVisitor the class visitor to which visits to + * classes that are marked to be escaping + * will be delegated. + */ + public EscapingClassFilter(ClassVisitor escapingClassVisitor) + { + this(escapingClassVisitor, null); + } + + + /** + * Creates a new EscapingClassFilter. + * @param escapingClassVisitor the class visitor to which visits to + * classes that are marked to be escaping + * will be delegated. + * @param otherClassVisitor the class visitor to which visits to + * classes that are not marked to be escaping + * will be delegated. + */ + public EscapingClassFilter(ClassVisitor escapingClassVisitor, + ClassVisitor otherClassVisitor) + { + this.escapingClassVisitor = escapingClassVisitor; + this.otherClassVisitor = otherClassVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Is the class marked to be escaping? + ClassVisitor classVisitor = EscapingClassMarker.isClassEscaping(libraryClass) ? + escapingClassVisitor : + otherClassVisitor; + + if (classVisitor != null) + { + classVisitor.visitLibraryClass(libraryClass); + } + } + + + public void visitProgramClass(ProgramClass programClass) + { + // Is the class marked to be escaping? + ClassVisitor classVisitor = EscapingClassMarker.isClassEscaping(programClass) ? + escapingClassVisitor : + otherClassVisitor; + + if (classVisitor != null) + { + classVisitor.visitProgramClass(programClass); + } + } +} diff --git a/core/src/proguard/optimize/info/EscapingClassMarker.java b/core/src/proguard/optimize/info/EscapingClassMarker.java new file mode 100644 index 000000000..a34c39af1 --- /dev/null +++ b/core/src/proguard/optimize/info/EscapingClassMarker.java @@ -0,0 +1,222 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.Instruction; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.ClassVisitor; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.evaluation.*; + +/** + * This AttributeVisitor marks the classes that are escaping from the visited + * code attributes. + * + * @see ReferenceEscapeChecker + * @author Eric Lafortune + */ +public class EscapingClassMarker +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ClassVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("ecm") != null; + //*/ + + + private final PartialEvaluator partialEvaluator; + private final boolean runPartialEvaluator; + private final ReferenceEscapeChecker referenceEscapeChecker; + private final boolean runReferenceEscapeChecker; + + + /** + * Creates a new EscapingClassMarker. + */ + public EscapingClassMarker() + { + // We need typed references. + this(new TypedReferenceValueFactory()); + } + + + /** + * Creates a new EscapingClassMarker. + */ + public EscapingClassMarker(ValueFactory valueFactory) + { + this(valueFactory, + new ReferenceTracingValueFactory(valueFactory)); + } + + + /** + * Creates a new EscapingClassMarker. + */ + public EscapingClassMarker(ValueFactory valueFactory, + ReferenceTracingValueFactory tracingValueFactory) + { + this(new PartialEvaluator(tracingValueFactory, + new ReferenceTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)), + true, + tracingValueFactory), + true); + } + + + /** + * Creates a new EscapingClassMarker. + */ + public EscapingClassMarker(PartialEvaluator partialEvaluator, + boolean runPartialEvaluator) + { + this(partialEvaluator, + runPartialEvaluator, + new ReferenceEscapeChecker(partialEvaluator, false), + true); + } + + + /** + * Creates a new EscapingClassMarker. + */ + public EscapingClassMarker(PartialEvaluator partialEvaluator, + boolean runPartialEvaluator, + ReferenceEscapeChecker referenceEscapeChecker, + boolean runReferenceEscapeChecker) + { + this.partialEvaluator = partialEvaluator; + this.runPartialEvaluator = runPartialEvaluator; + this.referenceEscapeChecker = referenceEscapeChecker; + this.runReferenceEscapeChecker = runReferenceEscapeChecker; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Evaluate the code. + if (runPartialEvaluator) + { + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + } + + if (runReferenceEscapeChecker) + { + referenceEscapeChecker.visitCodeAttribute(clazz, method, codeAttribute); + } + + // Mark all escaping classes. + codeAttribute.instructionsAccept(clazz, + method, + partialEvaluator.tracedInstructionFilter(this)); + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Does the instruction push a value that escapes? + // We'll also count values that are returned, since they may be + // downcast and the downcast type may escape in some calling + // method. + // TODO: Refine check: is a value is downcast to an escaping class, while it is being returned? + if (instruction.stackPushCount(clazz) == 1 && + (referenceEscapeChecker.isInstanceEscaping(offset) || + referenceEscapeChecker.isInstanceReturned(offset))) + { + TracedStack stackAfter = partialEvaluator.getStackAfter(offset); + Value stackEntry = stackAfter.getTop(0); + + // Is it really a reference type? + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + // Is it a plain class reference type? + ReferenceValue referenceValue = stackEntry.referenceValue(); + if (referenceValue.isNull() != Value.ALWAYS && + !ClassUtil.isInternalArrayType(referenceValue.getType())) + { + // Do we know the class? + Clazz referencedClass = referenceValue.getReferencedClass(); + if (referencedClass != null) + { + if (DEBUG) + { + System.out.println("EscapingClassMarker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+instruction.toString(offset)+" pushes escaping ["+referencedClass.getName()+"]"); + } + + // Mark it, along with its superclasses. + referencedClass.hierarchyAccept(true, true, true, false, this); + } + } + } + } + } + + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + public void visitProgramClass(ProgramClass programClass) + { + markClassEscaping(programClass); + } + + + // Small utility methods. + + /** + * Marks the given class as escaping. + */ + private void markClassEscaping(Clazz clazz) + { + ClassOptimizationInfo info = ProgramClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info instanceof ProgramClassOptimizationInfo) + { + ((ProgramClassOptimizationInfo)info).setEscaping(); + } + } + + + /** + * Returns whether the given class is escaping. + */ + public static boolean isClassEscaping(Clazz clazz) + { + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).isEscaping(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/ExceptionInstructionChecker.java b/core/src/proguard/optimize/info/ExceptionInstructionChecker.java similarity index 73% rename from src/proguard/optimize/info/ExceptionInstructionChecker.java rename to core/src/proguard/optimize/info/ExceptionInstructionChecker.java index 727139116..5b945c377 100644 --- a/src/proguard/optimize/info/ExceptionInstructionChecker.java +++ b/core/src/proguard/optimize/info/ExceptionInstructionChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -37,6 +37,13 @@ public class ExceptionInstructionChecker // ConstantVisitor, // MemberVisitor { + //* + private static final boolean DEBUG = false; + /*/ + public static boolean DEBUG = System.getProperty("eic") != null; + //*/ + + // A return value for the visitor methods. private boolean mayThrowExceptions; @@ -65,6 +72,34 @@ public boolean mayThrowExceptions(Clazz clazz, int startOffset, int endOffset) { + if (DEBUG) + { + System.out.println("ExceptionInstructionChecker.mayThrowExceptions ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+startOffset+" -> "+endOffset); + } + + return firstExceptionThrowingInstructionOffset(clazz, + method, + codeAttribute, + startOffset, + endOffset) < endOffset; + } + + + /** + * Returns the offset of the first instruction in the specified block of + * code that may throw exceptions, or the end offset if there is none. + */ + public int firstExceptionThrowingInstructionOffset(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int startOffset, + int endOffset) + { + if (DEBUG) + { + System.out.println("ExceptionInstructionChecker.firstExceptionThrowingInstructionOffset ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+startOffset+" -> "+endOffset); + } + byte[] code = codeAttribute.code; // Go over all instructions. @@ -81,14 +116,73 @@ public boolean mayThrowExceptions(Clazz clazz, offset, instruction)) { - return true; + if (DEBUG) + { + System.out.println(" "+instruction.toString(offset)); + } + + return offset; } // Go to the next instruction. offset += instruction.length(offset); } - return false; + return endOffset; + } + + + /** + * Returns the offset after the last instruction in the specified block of + * code that may throw exceptions, or the start offset if there is none. + */ + public int lastExceptionThrowingInstructionOffset(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int startOffset, + int endOffset) + { + if (DEBUG) + { + System.out.println("ExceptionInstructionChecker.lastExceptionThrowingInstructionOffset ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+startOffset+" -> "+endOffset); + } + + byte[] code = codeAttribute.code; + + int lastOffset = startOffset; + + // 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 (mayThrowExceptions(clazz, + method, + codeAttribute, + offset, + instruction)) + { + if (DEBUG) + { + System.out.println(" "+instruction.toString(offset)); + } + + // Go to the next instruction. + offset += instruction.length(offset); + + lastOffset = offset; + } + else + { + // Go to the next instruction. + offset += instruction.length(offset); + } + } + + return lastOffset; } diff --git a/core/src/proguard/optimize/info/FieldOptimizationInfo.java b/core/src/proguard/optimize/info/FieldOptimizationInfo.java new file mode 100644 index 000000000..cf8a56eb0 --- /dev/null +++ b/core/src/proguard/optimize/info/FieldOptimizationInfo.java @@ -0,0 +1,82 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.evaluation.value.*; + +/** + * This class stores some optimization information that can be attached to + * a field. + * + * @author Eric Lafortune + */ +public class FieldOptimizationInfo +extends SimplifiedVisitor +{ + public boolean isKept() + { + return true; + } + + + public boolean isWritten() + { + return true; + } + + + public boolean isRead() + { + return true; + } + + + public boolean canBeMadePrivate() + { + return false; + } + + + public ReferenceValue getReferencedClass() + { + return null; + } + + + public Value getValue() + { + return null; + } + + + public static void setFieldOptimizationInfo(Clazz clazz, Field field) + { + field.setVisitorInfo(new FieldOptimizationInfo()); + } + + + public static FieldOptimizationInfo getFieldOptimizationInfo(Field field) + { + return (FieldOptimizationInfo)field.getVisitorInfo(); + } +} diff --git a/src/proguard/optimize/info/InstanceofClassFilter.java b/core/src/proguard/optimize/info/InstanceofClassFilter.java similarity index 97% rename from src/proguard/optimize/info/InstanceofClassFilter.java rename to core/src/proguard/optimize/info/InstanceofClassFilter.java index 35d3b5d2c..0906914de 100644 --- a/src/proguard/optimize/info/InstanceofClassFilter.java +++ b/core/src/proguard/optimize/info/InstanceofClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/InstanceofClassMarker.java b/core/src/proguard/optimize/info/InstanceofClassMarker.java similarity index 80% rename from src/proguard/optimize/info/InstanceofClassMarker.java rename to core/src/proguard/optimize/info/InstanceofClassMarker.java index 26cc9665a..47ac03a34 100644 --- a/src/proguard/optimize/info/InstanceofClassMarker.java +++ b/core/src/proguard/optimize/info/InstanceofClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,6 +28,7 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; +import proguard.optimize.OptimizationInfoClassFilter; /** * This InstructionVisitor marks all classes that are used in an 'instanceof' @@ -41,6 +42,9 @@ public class InstanceofClassMarker ConstantVisitor, ClassVisitor { + private final OptimizationInfoClassFilter filteredClassMarker = new OptimizationInfoClassFilter(this); + + // Implementations for InstructionVisitor. public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} @@ -59,17 +63,15 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { - classConstant.referencedClassAccept(this); + classConstant.referencedClassAccept(filteredClassMarker); } // Implementations for ClassVisitor. - public void visitLibraryClass(LibraryClass libraryClass) {} - - public void visitProgramClass(ProgramClass programClass) + public void visitAnyClass(Clazz clazz) { - setInstanceofed(programClass); + setInstanceofed(clazz); } @@ -77,17 +79,12 @@ public void visitProgramClass(ProgramClass programClass) private static void setInstanceofed(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setInstanceofed(); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setInstanceofed(); } public static boolean isInstanceofed(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - return info == null || info.isInstanceofed(); + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).isInstanceofed(); } } \ No newline at end of file diff --git a/src/proguard/optimize/info/InstantiationClassFilter.java b/core/src/proguard/optimize/info/InstantiationClassFilter.java similarity index 97% rename from src/proguard/optimize/info/InstantiationClassFilter.java rename to core/src/proguard/optimize/info/InstantiationClassFilter.java index 804e9d0ca..cf9952a62 100644 --- a/src/proguard/optimize/info/InstantiationClassFilter.java +++ b/core/src/proguard/optimize/info/InstantiationClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/InstantiationClassMarker.java b/core/src/proguard/optimize/info/InstantiationClassMarker.java similarity index 80% rename from src/proguard/optimize/info/InstantiationClassMarker.java rename to core/src/proguard/optimize/info/InstantiationClassMarker.java index 610be97e4..577bfa5cc 100644 --- a/src/proguard/optimize/info/InstantiationClassMarker.java +++ b/core/src/proguard/optimize/info/InstantiationClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,6 +28,7 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; +import proguard.optimize.OptimizationInfoClassFilter; /** * This InstructionVisitor marks all classes that are instantiated by any of @@ -41,6 +42,9 @@ public class InstantiationClassMarker ConstantVisitor, ClassVisitor { + private final OptimizationInfoClassFilter filteredClassMarker = new OptimizationInfoClassFilter(this); + + // Implementations for InstructionVisitor. public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} @@ -59,17 +63,15 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { - classConstant.referencedClassAccept(this); + classConstant.referencedClassAccept(filteredClassMarker); } // Implementations for ClassVisitor. - public void visitLibraryClass(LibraryClass libraryClass) {} - - public void visitProgramClass(ProgramClass programClass) + public void visitAnyClass(Clazz clazz) { - setInstantiated(programClass); + setInstantiated(clazz); } @@ -77,17 +79,12 @@ public void visitProgramClass(ProgramClass programClass) private static void setInstantiated(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setInstantiated(); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setInstantiated(); } public static boolean isInstantiated(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - return info == null || info.isInstantiated(); + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).isInstantiated(); } } \ No newline at end of file diff --git a/src/proguard/optimize/info/MethodInvocationMarker.java b/core/src/proguard/optimize/info/MethodInvocationMarker.java similarity index 84% rename from src/proguard/optimize/info/MethodInvocationMarker.java rename to core/src/proguard/optimize/info/MethodInvocationMarker.java index 2288669df..bc5ec5b9c 100644 --- a/src/proguard/optimize/info/MethodInvocationMarker.java +++ b/core/src/proguard/optimize/info/MethodInvocationMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,6 +28,7 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.MemberVisitor; +import proguard.optimize.OptimizationInfoMemberFilter; /** * This InstructionVisitor counts the number of times methods are invoked from @@ -41,6 +42,9 @@ public class MethodInvocationMarker ConstantVisitor, MemberVisitor { + private final OptimizationInfoMemberFilter filteredMethodMarker = new OptimizationInfoMemberFilter(this); + + // Implementations for InstructionVisitor. public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} @@ -60,14 +64,14 @@ public void visitAnyConstant(Clazz clazz, Constant constant) {} public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { // Mark the referenced method, if any. - stringConstant.referencedMemberAccept(this); + stringConstant.referencedMemberAccept(filteredMethodMarker); } public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) { // Mark the referenced method. - refConstant.referencedMemberAccept(this); + refConstant.referencedMemberAccept(filteredMethodMarker); } @@ -86,11 +90,7 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM private static void incrementInvocationCount(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.incrementInvocationCount(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).incrementInvocationCount(); } @@ -100,8 +100,6 @@ private static void incrementInvocationCount(Method method) */ public static int getInvocationCount(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info != null ? info.getInvocationCount() : - Integer.MAX_VALUE; + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getInvocationCount(); } } diff --git a/core/src/proguard/optimize/info/MethodOptimizationInfo.java b/core/src/proguard/optimize/info/MethodOptimizationInfo.java new file mode 100644 index 000000000..50299a222 --- /dev/null +++ b/core/src/proguard/optimize/info/MethodOptimizationInfo.java @@ -0,0 +1,291 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.MethodLinker; +import proguard.evaluation.value.Value; + +/** + * This class stores some optimization information that can be attached to + * a method. + * + * @author Eric Lafortune + */ +public class MethodOptimizationInfo +{ + protected boolean hasNoSideEffects = false; + protected boolean hasNoExternalSideEffects = false; + protected boolean hasNoEscapingParameters = false; + protected boolean hasNoExternalReturnValues = false; + + + public boolean isKept() + { + return true; + } + + + public void setNoSideEffects() + { + hasNoSideEffects = true; + hasNoExternalSideEffects = true; + hasNoEscapingParameters = true; + } + + + public boolean hasNoSideEffects() + { + return hasNoSideEffects; + } + + + public void setNoExternalSideEffects() + { + hasNoExternalSideEffects = true; + hasNoEscapingParameters = true; + } + + + public boolean hasNoExternalSideEffects() + { + return hasNoExternalSideEffects; + } + + + public void setNoEscapingParameters() + { + hasNoEscapingParameters = true; + } + + + public boolean hasNoEscapingParameters() + { + return hasNoEscapingParameters; + } + + + public void setNoExternalReturnValues() + { + hasNoExternalReturnValues = true; + } + + + public boolean hasNoExternalReturnValues() + { + return hasNoExternalReturnValues; + } + + + // Methods that may be specialized. + + public boolean hasSideEffects() + { + return !hasNoSideEffects; + } + + + public boolean canBeMadePrivate() + { + return false; + } + + + public boolean catchesExceptions() + { + return true; + } + + + public boolean branchesBackward() + { + return true; + } + + + public boolean invokesSuperMethods() + { + return true; + } + + + public boolean invokesDynamically() + { + return true; + } + + + public boolean accessesPrivateCode() + { + return true; + } + + + public boolean accessesPackageCode() + { + return true; + } + + + public boolean accessesProtectedCode() + { + return true; + } + + + public boolean hasSynchronizedBlock() + { + return true; + } + + + public boolean returnsWithNonEmptyStack() + { + return false; + } + + + public int getInvocationCount() + { + return Integer.MAX_VALUE; + } + + + public int getParameterSize() + { + return 0; + } + + + public boolean hasUnusedParameters() + { + return false; + } + + + public boolean isParameterUsed(int variableIndex) + { + return true; + } + + + public long getUsedParameters() + { + return -1L; + } + + + public boolean hasParameterEscaped(int parameterIndex) + { + return true; + } + + + public long getEscapedParameters() + { + return -1L; + } + + + public boolean isParameterEscaping(int parameterIndex) + { + return !hasNoEscapingParameters; + } + + + public long getEscapingParameters() + { + return hasNoEscapingParameters ? 0L : -1L; + } + + + public boolean isParameterModified(int parameterIndex) + { + // TODO: Refine for static methods. + return + !hasNoSideEffects && + (!hasNoExternalSideEffects || parameterIndex == 0); + } + + + public long getModifiedParameters() + { + // TODO: Refine for static methods. + return + hasNoSideEffects ? 0L : + hasNoExternalSideEffects ? 1L : + -1L; + } + + + public boolean modifiesAnything() + { + return !hasNoExternalSideEffects; + } + + + public Value getParameterValue(int parameterIndex) + { + return null; + } + + + public boolean returnsParameter(int parameterIndex) + { + return true; + } + + + public long getReturnedParameters() + { + return -1L; + } + + + public boolean returnsNewInstances() + { + return true; + } + + + public boolean returnsExternalValues() + { + return !hasNoExternalReturnValues; + } + + + public Value getReturnValue() + { + return null; + } + + + public static void setMethodOptimizationInfo(Clazz clazz, Method method) + { + MethodLinker.lastMember(method).setVisitorInfo(new MethodOptimizationInfo()); + } + + + public static MethodOptimizationInfo getMethodOptimizationInfo(Method method) + { + return (MethodOptimizationInfo)MethodLinker.lastMember(method).getVisitorInfo(); + } +} diff --git a/core/src/proguard/optimize/info/MutableBoolean.java b/core/src/proguard/optimize/info/MutableBoolean.java new file mode 100644 index 000000000..a4f56ccf0 --- /dev/null +++ b/core/src/proguard/optimize/info/MutableBoolean.java @@ -0,0 +1,66 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; + +/** + * This class provides a mutable boolean flag. + */ +public class MutableBoolean +{ + private boolean flag; + /* + private int resetCounter; + private int setCounter; + private int totalCounter; + //*/ + + + public void set() + { + flag = true; + + /* + System.out.println("MutableBoolean.set: "+resetCounter+", "+setCounter++ +", "+totalCounter++); + if (totalCounter > 5000) + { + Thread.dumpStack(); + } + //*/ + } + + + public void reset() + { + flag = false; + + /* + resetCounter++; + setCounter = 0; + //*/ + } + + + public boolean isSet() + { + return flag; + } +} diff --git a/core/src/proguard/optimize/info/NoEscapingParametersMethodMarker.java b/core/src/proguard/optimize/info/NoEscapingParametersMethodMarker.java new file mode 100644 index 000000000..18e9dd086 --- /dev/null +++ b/core/src/proguard/optimize/info/NoEscapingParametersMethodMarker.java @@ -0,0 +1,72 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; + +/** + * This MemberVisitor marks all methods that it visits as not having any + * escaping parameters (including 'this'). It will make the + * ParameterEscapeMarker consider them as such without further analysis. + * + * @see ParameterEscapeMarker + * @author Eric Lafortune + */ +public class NoEscapingParametersMethodMarker +extends SimplifiedVisitor +implements MemberVisitor +{ + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz Clazz, Member member) + { + // Ignore any attempts to mark fields. + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + markNoParameterEscaping(programMethod); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + markNoParameterEscaping(libraryMethod); + } + + + // Small utility methods. + + private static void markNoParameterEscaping(Method method) + { + MethodOptimizationInfo.getMethodOptimizationInfo(method).setNoEscapingParameters(); + } + + + public static boolean hasNoParameterEscaping(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).hasNoEscapingParameters(); + } +} diff --git a/core/src/proguard/optimize/info/NoExternalReturnValuesMethodMarker.java b/core/src/proguard/optimize/info/NoExternalReturnValuesMethodMarker.java new file mode 100644 index 000000000..07419e3e9 --- /dev/null +++ b/core/src/proguard/optimize/info/NoExternalReturnValuesMethodMarker.java @@ -0,0 +1,73 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; + +/** + * This MemberVisitor marks all methods that it visits as not having any + * return values that are external reference values (only parameters or new + * instances). It will make the ParameterEscapeMarker consider them as + * such without further analysis. + * + * @see ParameterEscapeMarker + * @author Eric Lafortune + */ +public class NoExternalReturnValuesMethodMarker +extends SimplifiedVisitor +implements MemberVisitor +{ + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz Clazz, Member member) + { + // Ignore any attempts to mark fields. + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + markNoExternalReturnValues(programMethod); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + markNoExternalReturnValues(libraryMethod); + } + + + // Small utility methods. + + private static void markNoExternalReturnValues(Method method) + { + MethodOptimizationInfo.getMethodOptimizationInfo(method).setNoExternalReturnValues(); + } + + + public static boolean hasNoExternalReturnValues(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).hasNoExternalReturnValues(); + } +} diff --git a/core/src/proguard/optimize/info/NoExternalSideEffectMethodMarker.java b/core/src/proguard/optimize/info/NoExternalSideEffectMethodMarker.java new file mode 100644 index 000000000..f8b507f06 --- /dev/null +++ b/core/src/proguard/optimize/info/NoExternalSideEffectMethodMarker.java @@ -0,0 +1,72 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; + +/** + * This MemberVisitor marks all methods that it visits as not having any + * external side effects. It will make the SideEffectMethodMarker consider them + * as such without further analysis. + * + * @see SideEffectMethodMarker + * @author Eric Lafortune + */ +public class NoExternalSideEffectMethodMarker +extends SimplifiedVisitor +implements MemberVisitor +{ + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz Clazz, Member member) + { + // Ignore any attempts to mark fields. + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + markNoExternalSideEffects(programMethod); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + markNoExternalSideEffects(libraryMethod); + } + + + // Small utility methods. + + private static void markNoExternalSideEffects(Method method) + { + MethodOptimizationInfo.getMethodOptimizationInfo(method).setNoExternalSideEffects(); + } + + + public static boolean hasNoExternalSideEffects(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).hasNoExternalSideEffects(); + } +} diff --git a/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java b/core/src/proguard/optimize/info/NoSideEffectClassMarker.java similarity index 56% rename from src/proguard/optimize/info/StaticInitializerContainingClassMarker.java rename to core/src/proguard/optimize/info/NoSideEffectClassMarker.java index 49fb5432d..ea4c0edc6 100644 --- a/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java +++ b/core/src/proguard/optimize/info/NoSideEffectClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -18,48 +18,43 @@ * 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.Clazz; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; /** - * This ClassVisitor marks all classes that contain static initializers. + * This ClassVisitor marks all classes that it visits as not having any side + * effects. It will make the SideEffectClassMarker consider them as such + * without further analysis. * + * @see SideEffectMethodMarker * @author Eric Lafortune */ -public class StaticInitializerContainingClassMarker +public class NoSideEffectClassMarker extends SimplifiedVisitor implements ClassVisitor { - // Implementations for ClassVisitor. + // Implementations for MemberVisitor. public void visitAnyClass(Clazz clazz) { - if (clazz.findMethod(ClassConstants.METHOD_NAME_CLINIT, - ClassConstants.METHOD_TYPE_CLINIT) != null) - { - setStaticInitializer(clazz); - } + markNoSideEffects(clazz); } // Small utility methods. - private static void setStaticInitializer(Clazz clazz) + private static void markNoSideEffects(Clazz Clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setContainsStaticInitializer(); - } + ClassOptimizationInfo.getClassOptimizationInfo(Clazz).setNoSideEffects(); } - public static boolean containsStaticInitializer(Clazz clazz) + public static boolean hasNoSideEffects(Clazz Clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - return info == null || info.containsStaticInitializer(); + return ClassOptimizationInfo.getClassOptimizationInfo(Clazz).hasNoSideEffects(); } -} \ No newline at end of file +} diff --git a/src/proguard/optimize/info/NoSideEffectMethodMarker.java b/core/src/proguard/optimize/info/NoSideEffectMethodMarker.java similarity index 68% rename from src/proguard/optimize/info/NoSideEffectMethodMarker.java rename to core/src/proguard/optimize/info/NoSideEffectMethodMarker.java index 624b22a2e..e51faf9fe 100644 --- a/src/proguard/optimize/info/NoSideEffectMethodMarker.java +++ b/core/src/proguard/optimize/info/NoSideEffectMethodMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,7 +21,7 @@ package proguard.optimize.info; import proguard.classfile.*; -import proguard.classfile.util.*; +import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.MemberVisitor; /** @@ -36,11 +36,6 @@ 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) @@ -65,27 +60,12 @@ public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryM 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); - } + MethodOptimizationInfo.getMethodOptimizationInfo(method).setNoSideEffects(); } 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(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).hasNoSideEffects(); } } diff --git a/src/proguard/optimize/info/NonEmptyStackReturnMarker.java b/core/src/proguard/optimize/info/NonEmptyStackReturnMarker.java similarity index 89% rename from src/proguard/optimize/info/NonEmptyStackReturnMarker.java rename to core/src/proguard/optimize/info/NonEmptyStackReturnMarker.java index 12124126c..9e2194a30 100644 --- a/src/proguard/optimize/info/NonEmptyStackReturnMarker.java +++ b/core/src/proguard/optimize/info/NonEmptyStackReturnMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -99,17 +99,12 @@ private void markReturnWithNonEmptyStack(Method method, private static void setReturnsWithNonEmptyStack(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setReturnsWithNonEmptyStack(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setReturnsWithNonEmptyStack(); } public static boolean returnsWithNonEmptyStack(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || info.returnsWithNonEmptyStack(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).returnsWithNonEmptyStack(); } } diff --git a/src/proguard/optimize/info/NonPrivateMemberMarker.java b/core/src/proguard/optimize/info/NonPrivateMemberMarker.java similarity index 71% rename from src/proguard/optimize/info/NonPrivateMemberMarker.java rename to core/src/proguard/optimize/info/NonPrivateMemberMarker.java index 99056083f..9218c8f15 100644 --- a/src/proguard/optimize/info/NonPrivateMemberMarker.java +++ b/core/src/proguard/optimize/info/NonPrivateMemberMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,6 +25,7 @@ import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.*; +import proguard.optimize.OptimizationInfoMemberFilter; /** * This ClassVisitor marks all class members that can not be made private in the @@ -38,7 +39,9 @@ public class NonPrivateMemberMarker ConstantVisitor, MemberVisitor { - private final MethodImplementationFilter filteredMethodMarker = new MethodImplementationFilter(this); + private final MemberVisitor filteredMemberMarker = new OptimizationInfoMemberFilter(this); + private final MemberVisitor implementedMethodMarker = new OptimizationInfoMemberFilter( + new MethodImplementationFilter(this)); // Implementations for ClassVisitor. @@ -51,22 +54,15 @@ public void visitProgramClass(ProgramClass programClass) // Explicitly mark the method. programClass.methodAccept(ClassConstants.METHOD_NAME_CLINIT, ClassConstants.METHOD_TYPE_CLINIT, - this); + filteredMemberMarker); // Explicitly mark the parameterless method. programClass.methodAccept(ClassConstants.METHOD_NAME_INIT, ClassConstants.METHOD_TYPE_INIT, - this); + filteredMemberMarker); // Mark all methods that may have implementations. - programClass.methodsAccept(filteredMethodMarker); - } - - - public void visitLibraryClass(LibraryClass libraryClass) - { - // Go over all methods. - libraryClass.methodsAccept(this); + programClass.methodsAccept(implementedMethodMarker); } @@ -79,7 +75,7 @@ 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); + stringConstant.referencedMemberAccept(filteredMemberMarker); } @@ -95,7 +91,7 @@ public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) !refConstant.getClassName(clazz).equals(clazz.getName())) { // The referenced class member can never be made private. - refConstant.referencedMemberAccept(this); + refConstant.referencedMemberAccept(filteredMemberMarker); } } @@ -108,33 +104,17 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie } - 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(); - } + ProgramFieldOptimizationInfo.getProgramFieldOptimizationInfo(field).setCanNotBeMadePrivate(); } @@ -143,19 +123,13 @@ private static void markCanNotBeMadePrivate(Field field) */ public static boolean canBeMadePrivate(Field field) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - return info != null && - info.canBeMadePrivate(); + return FieldOptimizationInfo.getFieldOptimizationInfo(field).canBeMadePrivate(); } private static void markCanNotBeMadePrivate(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setCanNotBeMadePrivate(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setCanNotBeMadePrivate(); } @@ -164,8 +138,6 @@ private static void markCanNotBeMadePrivate(Method method) */ public static boolean canBeMadePrivate(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info != null && - info.canBeMadePrivate(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).canBeMadePrivate(); } } diff --git a/core/src/proguard/optimize/info/OptimizationCodeAttributeFilter.java b/core/src/proguard/optimize/info/OptimizationCodeAttributeFilter.java new file mode 100644 index 000000000..81c1e6d4c --- /dev/null +++ b/core/src/proguard/optimize/info/OptimizationCodeAttributeFilter.java @@ -0,0 +1,88 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; +import proguard.optimize.KeepMarker; + +/** + * This AttributeVisitor delegates calls for code attributes to another + * AttributeVisitor, but only if they can be optimized. + *

+ * Note: any other attribute will not be delegated. + *

+ * + * @author Thomas Neidhart + */ +public class OptimizationCodeAttributeFilter +extends SimplifiedVisitor +implements AttributeVisitor +{ + private final AttributeVisitor attributeVisitor; + private final AttributeVisitor otherAttributeVisitor; + + + /** + * Creates a new OptimizationCodeAttributeFilter. + * @param attributeVisitor the AttributeVisitor to which visits will + * be delegated. + */ + public OptimizationCodeAttributeFilter(AttributeVisitor attributeVisitor) + { + this(attributeVisitor, null); + } + + + /** + * Creates a new OptimizationCodeAttributeFilter. + * @param attributeVisitor the AttributeVisitor to which visits will + * be delegated if the code attribute can be optimized. + * @param otherAttributeVisitor the AttributeVisitor to which visits will + * be delegated if the code attribute must be kept. + */ + public OptimizationCodeAttributeFilter(AttributeVisitor attributeVisitor, + AttributeVisitor otherAttributeVisitor) + { + this.attributeVisitor = attributeVisitor; + this.otherAttributeVisitor = otherAttributeVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + AttributeVisitor visitor = !KeepMarker.isKept(codeAttribute) ? + attributeVisitor : otherAttributeVisitor; + + if (visitor != null) + { + visitor.visitCodeAttribute(clazz, method, codeAttribute); + } + } + +} \ No newline at end of file diff --git a/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java b/core/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java similarity index 84% rename from src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java rename to core/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java index 6ac7b60c9..169397039 100644 --- a/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java +++ b/core/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -69,17 +69,12 @@ public void visitAnyMember(Clazz clazz, Member member) private static void setPackageVisibleMembers(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setContainsPackageVisibleMembers(); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setContainsPackageVisibleMembers(); } public static boolean containsPackageVisibleMembers(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - return info == null || info.containsPackageVisibleMembers(); + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).containsPackageVisibleMembers(); } -} \ No newline at end of file +} diff --git a/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java b/core/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java similarity index 89% rename from src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java rename to core/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java index 86ba80859..4a98b8f2b 100644 --- a/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java +++ b/core/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -113,17 +113,12 @@ public void visitAnyMember(Clazz clazz, Member member) private static void setInvokesPackageVisibleMembers(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setInvokesPackageVisibleMembers(); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setInvokesPackageVisibleMembers(); } public static boolean invokesPackageVisibleMembers(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - return info == null || info.invokesPackageVisibleMembers(); + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).invokesPackageVisibleMembers(); } -} \ No newline at end of file +} diff --git a/core/src/proguard/optimize/info/ParameterEscapeMarker.java b/core/src/proguard/optimize/info/ParameterEscapeMarker.java new file mode 100644 index 000000000..710e2043d --- /dev/null +++ b/core/src/proguard/optimize/info/ParameterEscapeMarker.java @@ -0,0 +1,924 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.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.*; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.evaluation.*; + +/** + * This MemberVisitor, AttributeVisitor, and InstructionVisitor marks the + * reference parameters that are escaping, that are modified, or that are + * returned. + * + * It also marks methods that may modify anything on the heap. + * + * The class must be called as a MemberVisitor on all members (to mark the + * parameters of native methods, without code attributes), then as an + * AttributeVisitor on their code attributes (so it can run its PartialEvaluator + * and ReferenceEscapeChecker), and finally as an InstructionVisitor on its + * instructions (to actually mark the parameters). + * + * @see SideEffectClassChecker + * @see SideEffectClassMarker + * @author Eric Lafortune + */ +public class ParameterEscapeMarker +extends SimplifiedVisitor +implements MemberVisitor, + AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + ParameterVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("pem") != null; + //*/ + + + private final MutableBoolean repeatTrigger; + private final PartialEvaluator partialEvaluator; + private final boolean runPartialEvaluator; + private final ReferenceEscapeChecker referenceEscapeChecker; + private final boolean runReferenceEscapeChecker; + + private final MemberVisitor parameterMarker = new AllParameterVisitor(true, this); + + // Parameters and values for visitor methods. + private Method referencingMethod; + private int referencingOffset; + private int referencingPopCount; + private boolean isReturnValueEscaping; + private boolean isReturnValueModified; + + + /** + * Creates a new ParameterEscapeMarker. + */ + public ParameterEscapeMarker(MutableBoolean repeatTrigger) + { + this(repeatTrigger, + new BasicValueFactory()); + } + + + /** + * Creates a new ParameterEscapeMarker. + */ + public ParameterEscapeMarker(MutableBoolean repeatTrigger, + ValueFactory valueFactory) + { + this(repeatTrigger, + valueFactory, + new ReferenceTracingValueFactory(valueFactory)); + } + + + /** + * Creates a new ParameterEscapeMarker. + */ + public ParameterEscapeMarker(MutableBoolean repeatTrigger, + ValueFactory valueFactory, + ReferenceTracingValueFactory tracingValueFactory) + { + this(repeatTrigger, + new PartialEvaluator(tracingValueFactory, + new ParameterTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)), + true, + tracingValueFactory), + true); + } + + + /** + * Creates a new ParameterEscapeMarker. + */ + public ParameterEscapeMarker(MutableBoolean repeatTrigger, + PartialEvaluator partialEvaluator, + boolean runPartialEvaluator) + { + this(repeatTrigger, + partialEvaluator, + runPartialEvaluator, + new ReferenceEscapeChecker(partialEvaluator, false), + true); + } + + + /** + * Creates a new ParameterEscapeMarker. + */ + public ParameterEscapeMarker(MutableBoolean repeatTrigger, + PartialEvaluator partialEvaluator, + boolean runPartialEvaluator, + ReferenceEscapeChecker referenceEscapeChecker, + boolean runReferenceEscapeChecker) + { + this.repeatTrigger = repeatTrigger; + this.partialEvaluator = partialEvaluator; + this.runPartialEvaluator = runPartialEvaluator; + this.referenceEscapeChecker = referenceEscapeChecker; + this.runReferenceEscapeChecker = runReferenceEscapeChecker; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + int accessFlags = programMethod.getAccessFlags(); + + // Is it a native method? + if ((accessFlags & ClassConstants.ACC_NATIVE) != 0) + { + // Mark all parameters. + markModifiedParameters(programMethod, -1L); + markEscapingParameters(programMethod, -1L); + markReturnedParameters(programMethod, -1L); + markAnythingModified(programMethod); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Evaluate the code. + if (runPartialEvaluator) + { + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + } + + if (runReferenceEscapeChecker) + { + referenceEscapeChecker.visitCodeAttribute(clazz, method, codeAttribute); + } + + if (DEBUG) + { + // These results are not complete yet, since this class must still + // be called as an InstructionVisitor. + System.out.println("ParameterEscapeMarker: [" + clazz.getName() + "." + method.getName(clazz) + method.getDescriptor(clazz) + "]"); + + int parameterCount = + ClassUtil.internalMethodParameterCount(method.getDescriptor(clazz), + method.getAccessFlags()); + + for (int index = 0; index < parameterCount; index++) + { + System.out.println(" " + +// (hasParameterEscaped(method, index) ? 'e' : '.') + + (isParameterEscaping(method, index) ? 'E' : '.') + + (isParameterReturned(method, index) ? 'R' : '.') + + (isParameterModified(method, index) ? 'M' : '.') + + " P" + index); + } + + System.out.println(" " + + (returnsExternalValues(method) ? 'X' : '.') + + " Return value"); + } + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + case InstructionConstants.OP_AASTORE: + // Mark array parameters whose element is modified. + markModifiedParameters(method, + offset, + simpleInstruction.stackPopCount(clazz) - 1); + + // Mark reference values that are put in the array. + markEscapingParameters(method, offset, 0); + break; + + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + // Mark array parameters whose element is modified. + markModifiedParameters(method, + offset, + simpleInstruction.stackPopCount(clazz) - 1); + break; + + case InstructionConstants.OP_ARETURN: + // Mark returned reference values. + markReturnedParameters(clazz, method, offset, 0); + break; + + case InstructionConstants.OP_ATHROW: + // Mark the escaping reference values. + markEscapingParameters(method, offset, 0); + break; + } + } + + + 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_NEW: + case InstructionConstants.OP_ANEWARRAY: + case InstructionConstants.OP_MULTIANEWARRAY: + case InstructionConstants.OP_GETSTATIC: + // Mark possible modifications due to initializers. + referencingMethod = method; + referencingOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + + case InstructionConstants.OP_PUTSTATIC: + // Mark some global modification. + markAnythingModified(method); + + // Mark reference values that are put in the field. + markEscapingParameters(method, offset, 0); + break; + + case InstructionConstants.OP_PUTFIELD: + // Mark reference parameters whose field is modified. + markModifiedParameters(method, + offset, + constantInstruction.stackPopCount(clazz) - 1); + + // Mark reference values that are put in the field. + markEscapingParameters(method, offset, 0); + break; + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + // Mark reference parameters that are modified as parameters + // of the invoked method. + // Mark reference values that are escaping as parameters + // of the invoked method. + // Mark escaped reference parameters in the invoked method. + referencingMethod = method; + referencingOffset = offset; + referencingPopCount = constantInstruction.stackPopCount(clazz); + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + Clazz referencedClass = stringConstant.referencedClass; + + // If a static initializer may modify anything, so does the referencing + // method. + if (referencedClass == null || + SideEffectClassChecker.mayHaveSideEffects(clazz, + referencedClass)) + { + markAnythingModified(referencingMethod); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + Clazz referencedClass = classConstant.referencedClass; + + // If a static initializer may modify anything, so does the referencing + // method. + if (referencedClass == null || + SideEffectClassChecker.mayHaveSideEffects(clazz, + referencedClass)) + { + markAnythingModified(referencingMethod); + } + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + markAnythingModified(referencingMethod); + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + clazz.constantPoolEntryAccept(fieldrefConstant.u2classIndex, this); + } + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + Method referencedMethod = (Method)refConstant.referencedMember; + + // If the referenced method or a static initializer may modify anything, + // so does the referencing method. + if (referencedMethod == null || + modifiesAnything(referencedMethod) || + SideEffectClassChecker.mayHaveSideEffects(clazz, + refConstant.referencedClass, + referencedMethod)) + { + markAnythingModified(referencingMethod); + } + + // Do we know the invoked method? + if (referencedMethod == null) + { + // Mark all parameters of the invoking method that are passed to + // the invoked method, since they may escape or or be modified + // there. + for (int parameterOffset = 0; parameterOffset < referencingPopCount; parameterOffset++) + { + int stackEntryIndex = referencingPopCount - parameterOffset - 1; + + markEscapingParameters(referencingMethod, + referencingOffset, + stackEntryIndex); + + markModifiedParameters(referencingMethod, + referencingOffset, + stackEntryIndex); + } + } + else + { + // Remember whether the return value of the method is escaping or + // modified later on. + isReturnValueEscaping = + referenceEscapeChecker.isInstanceEscaping(referencingOffset); + + isReturnValueModified = + referenceEscapeChecker.isInstanceModified(referencingOffset); + + // Mark parameters of the invoking method that are passed to the + // invoked method and escaping or modified there. + refConstant.referencedMemberAccept(parameterMarker); + } + } + + + // Implementations for ParameterVisitor. + + public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) + { + if (!ClassUtil.isInternalPrimitiveType(parameterType.charAt(0))) + { + Method method = (Method)member; + + // Is the parameter escaping from the method, + // or is it returned and then escaping? + if (isParameterEscaping(method, parameterIndex) || + (isParameterReturned(method, parameterIndex) && + isReturnValueEscaping)) + { + markEscapingParameters(referencingMethod, + referencingOffset, + parameterSize - parameterOffset - 1); + } + + // Is the parameter being modified in the method. + // or is it returned and then modified? + if (isParameterModified(method, parameterIndex) || + (isParameterReturned(method, parameterIndex) && + isReturnValueModified)) + { + markModifiedParameters(referencingMethod, + referencingOffset, + parameterSize - parameterOffset - 1); + } + } + } + + + // Small utility methods. + + /** + * Marks the producing reference parameters (and the classes) of the + * specified stack entry at the given instruction offset. + */ + private void markEscapingParameters(Method method, + int consumerOffset, + int stackEntryIndex) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(consumerOffset); + Value stackEntry = stackBefore.getTop(stackEntryIndex); + + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + ReferenceValue referenceValue = stackEntry.referenceValue(); + + // The null reference value may not have a trace value. + if (referenceValue.isNull() != Value.ALWAYS) + { + markEscapingParameters(method, referenceValue); + } + } + } + + + /** + * Marks the producing parameters (and the classes) of the given + * reference value. + */ + private void markEscapingParameters(Method method, + ReferenceValue referenceValue) + { + TracedReferenceValue tracedReferenceValue = (TracedReferenceValue)referenceValue; + InstructionOffsetValue producers = tracedReferenceValue.getTraceValue().instructionOffsetValue(); + + int producerCount = producers.instructionOffsetCount(); + for (int index = 0; index < producerCount; index++) + { + if (producers.isMethodParameter(index)) + { + // We know exactly which parameter is escaping. + markParameterEscaping(method, producers.methodParameter(index)); + } + } + } + + + /** + * Marks the given parameter as escaping from the given method. + */ + private void markParameterEscaping(Method method, int parameterIndex) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + if (!methodOptimizationInfo.isParameterEscaping(parameterIndex) && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).setParameterEscaping(parameterIndex); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.isParameterEscaping(parameterIndex)) + { + repeatTrigger.set(); + } + } + } + + + /** + * Marks the given parameters as escaping from the given method. + */ + private void markEscapingParameters(Method method, long escapingParameters) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + long oldEscapingParameters = + methodOptimizationInfo.getEscapingParameters(); + + if ((~oldEscapingParameters & escapingParameters) != 0 && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).updateEscapingParameters(escapingParameters); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.getEscapingParameters() != oldEscapingParameters) + { + repeatTrigger.set(); + } + } + } + + + /** + * Returns whether the given parameter is escaping from the given method. + */ + public static boolean isParameterEscaping(Method method, int parameterIndex) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).isParameterEscaping(parameterIndex); + } + + + /** + * Returns which parameters are escaping from the given method. + */ + public static long getEscapingParameters(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getEscapingParameters(); + } + + + /** + * Marks the method and the returned reference parameters of the specified + * stack entry at the given instruction offset. + */ + private void markReturnedParameters(Clazz clazz, + Method method, + int returnOffset, + int stackEntryIndex) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(returnOffset); + Value stackEntry = stackBefore.getTop(stackEntryIndex); + + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + ReferenceValue referenceValue = stackEntry.referenceValue(); + + // The null reference value may not have a trace value. + if (referenceValue.isNull() != Value.ALWAYS && + mayReturnType(clazz, method, referenceValue)) + { + markReturnedParameters(method, referenceValue); + } + } + } + + + /** + * Marks the method and the producing parameters of the given reference + * value. + */ + private void markReturnedParameters(Method method, + ReferenceValue referenceValue) + { + TracedReferenceValue tracedReferenceValue = (TracedReferenceValue)referenceValue; + InstructionOffsetValue producers = tracedReferenceValue.getTraceValue().instructionOffsetValue(); + + int producerCount = producers.instructionOffsetCount(); + for (int index = 0; index < producerCount; index++) + { + if (producers.isMethodParameter(index)) + { + // We know exactly which parameter is returned. + markParameterReturned(method, producers.methodParameter(index)); + } + else if (producers.isFieldValue(index)) + { + markReturnsExternalValues(method); + } + else if (producers.isNewinstance(index) || + producers.isExceptionHandler(index)) + { + markReturnsNewInstances(method); + } + } + } + + + /** + * Marks the given parameter as returned from the given method. + */ + private void markParameterReturned(Method method, int parameterIndex) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + if (!methodOptimizationInfo.returnsParameter(parameterIndex) && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).setParameterReturned(parameterIndex); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.returnsParameter(parameterIndex)) + { + repeatTrigger.set(); + } + } + } + + + /** + * Marks the given parameters as returned from the given method. + */ + private void markReturnedParameters(Method method, long returnedParameters) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + long oldReturnedParameters = + methodOptimizationInfo.getReturnedParameters(); + + if ((~oldReturnedParameters & returnedParameters) != 0 && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).updateReturnedParameters(returnedParameters); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.getReturnedParameters() != oldReturnedParameters) + { + repeatTrigger.set(); + } + } + } + + + /** + * Returns whether the given parameter is returned from the given method. + */ + public static boolean isParameterReturned(Method method, int parameterIndex) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).returnsParameter(parameterIndex); + } + + + /** + * Returns which parameters are returned from the given method. + */ + public static long getReturnedParameters(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getReturnedParameters(); + } + + + /** + * Marks that the given method returns new instances (created inside the + * method). + */ + private void markReturnsNewInstances(Method method) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + if (!methodOptimizationInfo.returnsNewInstances() && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).setReturnsNewInstances(); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.returnsNewInstances()) + { + repeatTrigger.set(); + } + } + } + + + /** + * Returns whether the given method returns new instances (created inside + * the method). + */ + public static boolean returnsNewInstances(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).returnsNewInstances(); + } + + + /** + * Marks that the given method returns external reference values (not + * parameter or new instance). + */ + private void markReturnsExternalValues(Method method) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + if (!methodOptimizationInfo.returnsExternalValues() && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).setReturnsExternalValues(); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.returnsExternalValues()) + { + repeatTrigger.set(); + } + } + } + + + /** + * Returns whether the given method returns external reference values + * (not parameter or new instance). + */ + public static boolean returnsExternalValues(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).returnsExternalValues(); + } + + + /** + * Returns whether the given method may return the given type of reference + * value + */ + private boolean mayReturnType(Clazz clazz, + Method method, + ReferenceValue referenceValue) + { + String returnType = + ClassUtil.internalMethodReturnType(method.getDescriptor(clazz)); + + Clazz[] referencedClasses = method instanceof ProgramMethod ? + ((ProgramMethod)method).referencedClasses : + ((LibraryMethod)method).referencedClasses; + + Clazz referencedClass = + referencedClasses == null || + !ClassUtil.isInternalClassType(returnType) ? null : + referencedClasses[referencedClasses.length - 1]; + + return referenceValue.instanceOf(returnType, + referencedClass) != Value.NEVER; + } + + + /** + * Marks the producing reference parameters of the specified stack entry at + * the given instruction offset. + */ + private void markModifiedParameters(Method method, + int offset, + int stackEntryIndex) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(offset); + Value stackEntry = stackBefore.getTop(stackEntryIndex); + + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + ReferenceValue referenceValue = stackEntry.referenceValue(); + + // The null reference value may not have a trace value. + if (referenceValue.isNull() != Value.ALWAYS) + { + markModifiedParameters(method, referenceValue); + } + } + } + + + /** + * Marks the producing parameters of the given reference value. + */ + private void markModifiedParameters(Method method, + ReferenceValue referenceValue) + { + TracedReferenceValue tracedReferenceValue = (TracedReferenceValue)referenceValue; + InstructionOffsetValue producers = tracedReferenceValue.getTraceValue().instructionOffsetValue(); + + int producerCount = producers.instructionOffsetCount(); + for (int index = 0; index < producerCount; index++) + { + if (producers.isMethodParameter(index)) + { + // We know exactly which parameter is being modified. + markParameterModified(method, producers.methodParameter(index)); + } + else if (!producers.isNewinstance(index) && + !producers.isExceptionHandler(index)) + { + // If some unknown instance is modified, any escaping parameters + // may be modified. + markModifiedParameters(method, getEscapingParameters(method)); + markAnythingModified(method); + } + } + } + + + /** + * Marks the given parameter as modified by the given method. + */ + private void markParameterModified(Method method, int parameterIndex) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + if (!methodOptimizationInfo.isParameterModified(parameterIndex) && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).setParameterModified(parameterIndex); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.isParameterModified(parameterIndex)) + { + repeatTrigger.set(); + } + } + } + + + /** + * Marks the given parameters as modified by the given method. + */ + private void markModifiedParameters(Method method, long modifiedParameters) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + long oldModifiedParameters = + methodOptimizationInfo.getModifiedParameters(); + + if ((~oldModifiedParameters & modifiedParameters) != 0 && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).updateModifiedParameters(modifiedParameters); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.getModifiedParameters() != oldModifiedParameters) + { + repeatTrigger.set(); + } + } + } + + + /** + * Returns whether the given parameter is modified by the given method. + */ + public static boolean isParameterModified(Method method, int parameterIndex) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).isParameterModified(parameterIndex); + } + + + /** + * Returns which parameters are modified by the given method. + */ + public static long getModifiedParameters(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getModifiedParameters(); + } + + + /** + * Marks that anything may be modified by the given method. + */ + private void markAnythingModified(Method method) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + if (!methodOptimizationInfo.modifiesAnything() && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).setModifiesAnything(); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.modifiesAnything()) + { + repeatTrigger.set(); + } + } + } + + + /** + * Returns whether anything may be modified by the given method. This takes + * into account the side effects of static initializers, except the static + * initializer of the invoked method (because it is better checked + * explicitly as a function of the referencing class). + * + * @see SideEffectClassChecker#mayHaveSideEffects(Clazz, Clazz, Member) + */ + public static boolean modifiesAnything(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).modifiesAnything(); + } +} \ No newline at end of file diff --git a/core/src/proguard/optimize/info/ParameterEscapedMarker.java b/core/src/proguard/optimize/info/ParameterEscapedMarker.java new file mode 100644 index 000000000..04faf1fd9 --- /dev/null +++ b/core/src/proguard/optimize/info/ParameterEscapedMarker.java @@ -0,0 +1,307 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; +import proguard.classfile.visitor.*; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.evaluation.*; + +/** + * This ClassPoolVisitor marks the reference parameters that have escaped or + * that are escaping, outside or inside their methods. + * + * @see ReferenceEscapeChecker + * @see ParameterEscapeMarker + * @author Eric Lafortune + */ +public class ParameterEscapedMarker +extends SimplifiedVisitor +implements ClassPoolVisitor, + ClassVisitor, + MemberVisitor, + AttributeVisitor, + InstructionVisitor, + ConstantVisitor +{ + /* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("pem") != null; + //*/ + + + private final ClassVisitor parameterEscapedMarker = + new AllMethodVisitor( + new AllAttributeVisitor(this)); + private final ValueFactory valueFactory = new BasicValueFactory(); + private final ReferenceTracingValueFactory tracingValueFactory = new ReferenceTracingValueFactory(valueFactory); + private final PartialEvaluator partialEvaluator = + new PartialEvaluator(tracingValueFactory, + new ParameterTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)), + true, + tracingValueFactory); + private final ReferenceEscapeChecker referenceEscapeChecker = new ReferenceEscapeChecker(partialEvaluator, false); + + // Parameters and values for visitor methods. + private boolean newEscapes; + private Method referencingMethod; + private int referencingOffset; + private int referencingPopCount; + + + /** + * Creates a new ParameterModificationMarker. + */ + public ParameterEscapedMarker() + { + } + + + // Implementations for ClassPoolVisitor. + + public void visitClassPool(ClassPool classPool) + { + // Go over all classes and their methods, marking if parameters are + // modified, until no new cases can be found. + do + { + newEscapes = false; + + if (DEBUG) + { + System.out.println("ParameterEscapedMarker: new iteration"); + } + + // Go over all classes and their methods once. + classPool.classesAccept(parameterEscapedMarker); + } + while (newEscapes); + + if (DEBUG) + { + classPool.classesAccept(new AllMethodVisitor(this)); + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (DEBUG) + { + System.out.println("ParameterEscapedMarker: [" + programClass.getName() + "." + programMethod.getName(programClass) + programMethod.getDescriptor(programClass) + "]"); + + int parameterSize = + ClassUtil.internalMethodParameterSize(programMethod.getDescriptor(programClass), + programMethod.getAccessFlags()); + + for (int index = 0; index < parameterSize; index++) + { + System.out.println(" " + + (hasParameterEscaped(programMethod, index) ? 'e' : '.') + + " P" + index); + } + } + } + + + // 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); + referenceEscapeChecker.visitCodeAttribute(clazz, method, codeAttribute); + + // Mark the parameters that are modified from the code. + codeAttribute.instructionsAccept(clazz, method, partialEvaluator.tracedInstructionFilter(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: + // Mark escaped reference parameters in the invoked method. + referencingMethod = method; + referencingOffset = offset; + referencingPopCount = constantInstruction.stackPopCount(clazz); + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + Method referencedMethod = (Method)refConstant.referencedMember; + + if (referencedMethod != null && + MethodOptimizationInfo.getMethodOptimizationInfo(referencedMethod) instanceof ProgramMethodOptimizationInfo) + { + // Mark reference parameters that are passed to the method. + for (int parameterIndex = 0; parameterIndex < referencingPopCount; parameterIndex++) + { + int stackEntryIndex = referencingPopCount - parameterIndex - 1; + + TracedStack stackBefore = partialEvaluator.getStackBefore(referencingOffset); + Value stackEntry = stackBefore.getTop(stackEntryIndex); + + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + // Has the parameter escaped outside or inside the referencing + // method? + if (hasEscapedBefore(referencingOffset, stackEntryIndex)) + { + markParameterEscaped(referencedMethod, parameterIndex); + } + } + } + } + } + + + // Small utility methods. + + /** + * Returns whether any of the producing reference values of the specified + * stack entry before the given instruction offset are escaping or have + * escaped. + */ + private boolean hasEscapedBefore(int instructionOffset, + int stackEntryIndex) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(instructionOffset); + Value stackEntry = stackBefore.getTop(stackEntryIndex); + + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + ReferenceValue referenceValue = stackEntry.referenceValue(); + + // The null reference value may not have a trace value. + if (referenceValue.isNull() != Value.ALWAYS && + hasEscaped(referenceValue)) + { + return true; + } + } + + return false; + } + + + /** + * Returns whether the producing reference value is escaping or has escaped. + */ + private boolean hasEscaped(ReferenceValue referenceValue) + { + TracedReferenceValue tracedReferenceValue = (TracedReferenceValue)referenceValue; + InstructionOffsetValue instructionOffsetValue = tracedReferenceValue.getTraceValue().instructionOffsetValue(); + + int count = instructionOffsetValue.instructionOffsetCount(); + for (int index = 0; index < count; index++) + { + if (instructionOffsetValue.isMethodParameter(index) ? + hasParameterEscaped(referencingMethod, instructionOffsetValue.methodParameter(index)) : + referenceEscapeChecker.isInstanceEscaping(instructionOffsetValue.instructionOffset(index))) + { + return true; + } + } + + return false; + } + + + /** + * Marks the given parameter as escaped from the given method. + */ + private void markParameterEscaped(Method method, int parameterIndex) + { + ProgramMethodOptimizationInfo info = ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method); + if (!info.hasParameterEscaped(parameterIndex)) + { + info.setParameterEscaped(parameterIndex); + + newEscapes = true; + } + } + + + /** + * Marks the given parameters as escaped from the given method. + */ + private void markEscapedParameters(Method method, long escapedParameters) + { + ProgramMethodOptimizationInfo info = ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method); + if ((~info.getEscapedParameters() & escapedParameters) != 0) + { + info.updateEscapedParameters(escapedParameters); + + newEscapes = true; + } + } + + + /** + * Returns whether the given parameter is escaped from the given method. + */ + public static boolean hasParameterEscaped(Method method, int parameterIndex) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).hasParameterEscaped(parameterIndex); + } + + + /** + * Returns which parameters are escaped from the given method. + */ + public static long getEscapedParameters(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getEscapedParameters(); + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/ParameterUsageMarker.java b/core/src/proguard/optimize/info/ParameterUsageMarker.java similarity index 71% rename from src/proguard/optimize/info/ParameterUsageMarker.java rename to core/src/proguard/optimize/info/ParameterUsageMarker.java index 85e348cc4..d7be40ac4 100644 --- a/src/proguard/optimize/info/ParameterUsageMarker.java +++ b/core/src/proguard/optimize/info/ParameterUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,7 +27,7 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; -import proguard.evaluation.value.Value; +import proguard.evaluation.value.*; import proguard.optimize.evaluation.PartialEvaluator; /** @@ -43,11 +43,16 @@ public class ParameterUsageMarker AttributeVisitor, InstructionVisitor { + //* private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("pum") != null; + //*/ private final boolean markThisParameter; private final boolean markAllParameters; + private final boolean analyzeCode; private final PartialEvaluator partialEvaluator = new PartialEvaluator(); @@ -69,9 +74,27 @@ public ParameterUsageMarker() */ public ParameterUsageMarker(boolean markThisParameter, boolean markAllParameters) + { + this(markThisParameter, markAllParameters, true); + } + + + /** + * 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. + * @param analyzeCode specifies whether the code of visited methods + * should be analyzed for used parameters. + */ + public ParameterUsageMarker(boolean markThisParameter, + boolean markAllParameters, + boolean analyzeCode) { this.markThisParameter = markThisParameter; this.markAllParameters = markAllParameters; + this.analyzeCode = analyzeCode; } @@ -125,23 +148,26 @@ else if ((accessFlags & ClassConstants.ACC_ABSTRACT) != 0) // other implementations, or is it a class instance initializer? if ((accessFlags & ClassConstants.ACC_STATIC) == 0 && ((accessFlags & ClassConstants.ACC_SYNCHRONIZED) != 0 || - programClass.mayHaveImplementations(programMethod) || + programClass.mayHaveImplementations(programMethod) || programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT))) { // Mark the 'this' parameter. markParameterUsed(programMethod, 0); } - // Mark the parameters that are used by the code. - programMethod.attributesAccept(programClass, this); + if (analyzeCode) + { + // 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++) + for (int variableIndex = 0; variableIndex < parameterSize; variableIndex++) { - System.out.print(isParameterUsed(programMethod, index) ? '+' : '-'); + System.out.print(isParameterUsed(programMethod, variableIndex) ? '+' : '-'); } System.out.println(); } @@ -190,21 +216,25 @@ public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute c if (partialEvaluator.isTraced(offset) && variableInstruction.isLoad()) { - int parameterIndex = variableInstruction.variableIndex; - if (parameterIndex < codeAttribute.u2maxLocals) + int variableIndex = variableInstruction.variableIndex; + if (variableIndex < codeAttribute.u2maxLocals) { + // The parameter indices stored in the producer values are + // parameter offsets, taking into account Category 2 types, + // and therefore compatible with variable indices. Value producer = - partialEvaluator.getVariablesBefore(offset).getProducerValue(parameterIndex); + partialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex); if (producer != null && - producer.instructionOffsetValue().contains(PartialEvaluator.AT_METHOD_ENTRY)) + producer.instructionOffsetValue().contains(variableIndex | InstructionOffsetValue.METHOD_PARAMETER)) { // Mark the variable. - markParameterUsed(method, parameterIndex); + markParameterUsed(method, variableIndex); // Account for Category 2 instructions, which take up two entries. - if (variableInstruction.isCategory2()) + if (variableInstruction.stackPopCount(clazz) == 2 || + variableInstruction.stackPushCount(clazz) == 2) { - markParameterUsed(method, parameterIndex + 1); + markParameterUsed(method, variableIndex + 1); } } } @@ -219,11 +249,7 @@ public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute c */ private static void setParameterSize(Method method, int parameterSize) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setParameterSize(parameterSize); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setParameterSize(parameterSize); } @@ -232,8 +258,7 @@ private static void setParameterSize(Method method, int parameterSize) */ public static int getParameterSize(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info != null ? info.getParameterSize() : 0; + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getParameterSize(); } @@ -242,24 +267,25 @@ public static int getParameterSize(Method method) */ public static void markParameterUsed(Method method, int variableIndex) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setParameterUsed(variableIndex); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setParameterUsed(variableIndex); } /** * Marks the given parameters as being used. */ - public static void markUsedParameters(Method method, long usedParameters) + private static void markUsedParameters(Method method, long usedParameters) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setUsedParameters(info.getUsedParameters() | usedParameters); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).updateUsedParameters(usedParameters); + } + + + /** + * Returns whether the given method has any unused parameters. + */ + public static boolean hasUnusedParameters(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).hasUnusedParameters(); } @@ -268,9 +294,7 @@ public static void markUsedParameters(Method method, long usedParameters) */ public static boolean isParameterUsed(Method method, int variableIndex) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || - info.isParameterUsed(variableIndex); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).isParameterUsed(variableIndex); } @@ -279,7 +303,6 @@ public static boolean isParameterUsed(Method method, int variableIndex) */ public static long getUsedParameters(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info != null ? info.getUsedParameters() : -1L; + return MethodOptimizationInfo.getMethodOptimizationInfo(method).getUsedParameters(); } } diff --git a/src/proguard/optimize/info/ClassOptimizationInfo.java b/core/src/proguard/optimize/info/ProgramClassOptimizationInfo.java similarity index 56% rename from src/proguard/optimize/info/ClassOptimizationInfo.java rename to core/src/proguard/optimize/info/ProgramClassOptimizationInfo.java index 4bf1e9fd6..0851cd15e 100644 --- a/src/proguard/optimize/info/ClassOptimizationInfo.java +++ b/core/src/proguard/optimize/info/ProgramClassOptimizationInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,17 +28,27 @@ * * @author Eric Lafortune */ -public class ClassOptimizationInfo +public class ProgramClassOptimizationInfo +extends ClassOptimizationInfo { - private boolean isInstantiated = false; - private boolean isInstanceofed = false; - private boolean isDotClassed = false; - private boolean isCaught = false; - private boolean isSimpleEnum = false; - private boolean containsStaticInitializer = false; - private boolean containsPackageVisibleMembers = false; - private boolean invokesPackageVisibleMembers = false; - private Clazz targetClass; + private volatile boolean isInstantiated = false; + private volatile boolean isInstanceofed = false; + private volatile boolean isDotClassed = false; + private volatile boolean isCaught = false; + private volatile boolean isSimpleEnum = false; + private volatile boolean isEscaping = false; + private volatile boolean hasSideEffects = false; + private volatile boolean containsPackageVisibleMembers = false; + private volatile boolean invokesPackageVisibleMembers = false; + private volatile boolean mayBeMerged = true; + private volatile Clazz wrappedClass; + private volatile Clazz targetClass; + + + public boolean isKept() + { + return false; + } public void setInstantiated() @@ -101,15 +111,27 @@ public boolean isSimpleEnum() } - public void setContainsStaticInitializer() + public void setEscaping() + { + isEscaping = true; + } + + + public boolean isEscaping() { - containsStaticInitializer = true; + return isEscaping; } - public boolean containsStaticInitializer() + public void setSideEffects() { - return containsStaticInitializer; + hasSideEffects = true; + } + + + public boolean hasSideEffects() + { + return !hasNoSideEffects && hasSideEffects; } @@ -137,6 +159,30 @@ public boolean invokesPackageVisibleMembers() } + public void setMayNotBeMerged() + { + mayBeMerged = false; + } + + + public boolean mayBeMerged() + { + return mayBeMerged; + } + + + public void setWrappedClass(Clazz wrappedClass) + { + this.wrappedClass = wrappedClass; + } + + + public Clazz getWrappedClass() + { + return wrappedClass; + } + + public void setTargetClass(Clazz targetClass) { this.targetClass = targetClass; @@ -151,27 +197,26 @@ public Clazz getTargetClass() 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; + this.isInstantiated |= other.isInstantiated(); + this.isInstanceofed |= other.isInstanceofed(); + this.isDotClassed |= other.isDotClassed(); + this.isCaught |= other.isCaught(); + this.isSimpleEnum |= other.isSimpleEnum(); + this.isEscaping |= other.isEscaping(); + this.hasSideEffects |= other.hasSideEffects(); + this.containsPackageVisibleMembers |= other.containsPackageVisibleMembers(); + this.invokesPackageVisibleMembers |= other.invokesPackageVisibleMembers(); } - public static void setClassOptimizationInfo(Clazz clazz) + public static void setProgramClassOptimizationInfo(Clazz clazz) { - clazz.setVisitorInfo(new ClassOptimizationInfo()); + clazz.setVisitorInfo(new ProgramClassOptimizationInfo()); } - public static ClassOptimizationInfo getClassOptimizationInfo(Clazz clazz) + public static ProgramClassOptimizationInfo getProgramClassOptimizationInfo(Clazz clazz) { - Object visitorInfo = clazz.getVisitorInfo(); - return visitorInfo instanceof ClassOptimizationInfo ? - (ClassOptimizationInfo)visitorInfo : - null; + return (ProgramClassOptimizationInfo)clazz.getVisitorInfo(); } } diff --git a/core/src/proguard/optimize/info/ProgramClassOptimizationInfoSetter.java b/core/src/proguard/optimize/info/ProgramClassOptimizationInfoSetter.java new file mode 100644 index 000000000..5b6d37c19 --- /dev/null +++ b/core/src/proguard/optimize/info/ProgramClassOptimizationInfoSetter.java @@ -0,0 +1,70 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.ProgramClass; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor attaches a ProgramClassOptimizationInfo instance to every + * class that is not being kept that it visits. + * + * @author Eric Lafortune + */ +public class ProgramClassOptimizationInfoSetter +extends SimplifiedVisitor +implements ClassVisitor +{ + private final boolean overwrite; + + + /** + * Creates a new ProgramClassOptimizationInfoSetter. + * Existing visitor info is not overridden. + */ + public ProgramClassOptimizationInfoSetter() + { + this(false); + } + + + /** + * Creates a new ProgramClassOptimizationInfoSetter. + * + * @param overwrite true if existing visitor info should be overridden, + * false otherwise. + */ + public ProgramClassOptimizationInfoSetter(boolean overwrite) + { + this.overwrite = overwrite; + } + + // Implementations for MemberVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (programClass.getVisitorInfo() == null || overwrite) + { + ProgramClassOptimizationInfo.setProgramClassOptimizationInfo(programClass); + } + } +} diff --git a/src/proguard/optimize/info/FieldOptimizationInfo.java b/core/src/proguard/optimize/info/ProgramFieldOptimizationInfo.java similarity index 65% rename from src/proguard/optimize/info/FieldOptimizationInfo.java rename to core/src/proguard/optimize/info/ProgramFieldOptimizationInfo.java index 5be9ce7df..07d1bfc64 100644 --- a/src/proguard/optimize/info/FieldOptimizationInfo.java +++ b/core/src/proguard/optimize/info/ProgramFieldOptimizationInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,7 +23,6 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.visitor.*; -import proguard.classfile.util.SimplifiedVisitor; import proguard.evaluation.ConstantValueFactory; import proguard.evaluation.value.*; @@ -33,22 +32,23 @@ * * @author Eric Lafortune */ -public class FieldOptimizationInfo -extends SimplifiedVisitor +public class ProgramFieldOptimizationInfo +extends FieldOptimizationInfo implements AttributeVisitor { - private static final ParticularValueFactory VALUE_FACTORY = new ParticularValueFactory(); - private static final ConstantValueFactory CONSTANT_VALUE_FACTORY = new ConstantValueFactory(VALUE_FACTORY); - private static final InitialValueFactory INITIAL_VALUE_FACTORY = new InitialValueFactory(VALUE_FACTORY); + private static final ValueFactory VALUE_FACTORY = new ParticularValueFactory(); + private static final ConstantValueFactory CONSTANT_VALUE_FACTORY = new ConstantValueFactory(VALUE_FACTORY); + private static final InitialValueFactory INITIAL_VALUE_FACTORY = new InitialValueFactory(VALUE_FACTORY); - private boolean isWritten; - private boolean isRead; - private boolean canBeMadePrivate = true; - private ReferenceValue referencedClass; - private Value value; + private volatile boolean isWritten; + private volatile boolean isRead; + private volatile boolean canBeMadePrivate = true; + private volatile ReferenceValue referencedClass; + private volatile Value value; - public FieldOptimizationInfo(Clazz clazz, Field field) + + public ProgramFieldOptimizationInfo(Clazz clazz, Field field) { int accessFlags = field.getAccessFlags(); @@ -59,13 +59,19 @@ public FieldOptimizationInfo(Clazz clazz, Field field) } - public FieldOptimizationInfo(FieldOptimizationInfo FieldOptimizationInfo) + public ProgramFieldOptimizationInfo(ProgramFieldOptimizationInfo programFieldOptimizationInfo) + { + this.isWritten = programFieldOptimizationInfo.isWritten; + this.isRead = programFieldOptimizationInfo.isRead; + this.canBeMadePrivate = programFieldOptimizationInfo.canBeMadePrivate; + this.referencedClass = programFieldOptimizationInfo.referencedClass; + this.value = programFieldOptimizationInfo.value; + } + + + public boolean isKept() { - this.isWritten = FieldOptimizationInfo.isWritten; - this.isRead = FieldOptimizationInfo.isRead; - this.canBeMadePrivate = FieldOptimizationInfo.canBeMadePrivate; - this.referencedClass = FieldOptimizationInfo.referencedClass; - this.value = FieldOptimizationInfo.value; + return false; } @@ -105,7 +111,7 @@ public boolean canBeMadePrivate() } - public void generalizeReferencedClass(ReferenceValue referencedClass) + public synchronized void generalizeReferencedClass(ReferenceValue referencedClass) { this.referencedClass = this.referencedClass != null ? this.referencedClass.generalize(referencedClass) : @@ -125,7 +131,7 @@ public void resetValue(Clazz clazz, Field field) value = null; - // See if we can initialize a static field with a constant value. + // See if we can initialize the static field with a constant value. if ((accessFlags & ClassConstants.ACC_STATIC) != 0) { field.accept(clazz, new AllAttributeVisitor(this)); @@ -138,12 +144,13 @@ public void resetValue(Clazz clazz, Field field) (SideEffectInstructionChecker.OPTIMIZE_CONSERVATIVELY || (accessFlags & ClassConstants.ACC_FINAL) == 0)) { + // Otherwise initialize the non-final field with the default value. value = INITIAL_VALUE_FACTORY.createValue(field.getDescriptor(clazz)); } } - public void generalizeValue(Value value) + public synchronized void generalizeValue(Value value) { this.value = this.value != null ? this.value.generalize(value) : @@ -171,18 +178,14 @@ public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueA // Small utility methods. - public static void setFieldOptimizationInfo(Clazz clazz, Field field) + public static void setProgramFieldOptimizationInfo(Clazz clazz, Field field) { - field.setVisitorInfo(new FieldOptimizationInfo(clazz, field)); + field.setVisitorInfo(new ProgramFieldOptimizationInfo(clazz, field)); } - public static FieldOptimizationInfo getFieldOptimizationInfo(Field field) + public static ProgramFieldOptimizationInfo getProgramFieldOptimizationInfo(Field field) { - Object visitorInfo = field.getVisitorInfo(); - - return visitorInfo instanceof FieldOptimizationInfo ? - (FieldOptimizationInfo)visitorInfo : - null; + return (ProgramFieldOptimizationInfo)field.getVisitorInfo(); } } diff --git a/core/src/proguard/optimize/info/ProgramMemberOptimizationInfoSetter.java b/core/src/proguard/optimize/info/ProgramMemberOptimizationInfoSetter.java new file mode 100644 index 000000000..7c6f9802c --- /dev/null +++ b/core/src/proguard/optimize/info/ProgramMemberOptimizationInfoSetter.java @@ -0,0 +1,83 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 attaches a ProgramFieldOptimizationInfo instance to every + * field and a ProgramMethodOptimizationInfo instance to every method that is + * not being kept that it visits. + * + * @author Eric Lafortune + */ +public class ProgramMemberOptimizationInfoSetter +extends SimplifiedVisitor +implements MemberVisitor +{ + private final boolean overwrite; + + + /** + * Creates a new ProgramMemberOptimizationInfoSetter that only attaches a + * ProgramFieldOptimizationInfo to a member if no other info is present + * on the member yet. + */ + public ProgramMemberOptimizationInfoSetter() + { + this(false); + } + + + /** + * Creates a new ProgramMemberOptimizationInfoSetter. + * + * @param overwrite boolean indicating whether an existing visitor info on + * a visited member should be overwritten or not. + */ + public ProgramMemberOptimizationInfoSetter(boolean overwrite) + { + this.overwrite = overwrite; + } + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (programField.getVisitorInfo() == null || overwrite) + { + ProgramFieldOptimizationInfo.setProgramFieldOptimizationInfo(programClass, + programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (MethodLinker.lastMember(programMethod).getVisitorInfo() == null || overwrite) + { + ProgramMethodOptimizationInfo.setProgramMethodOptimizationInfo(programClass, + programMethod); + } + } +} diff --git a/core/src/proguard/optimize/info/ProgramMethodOptimizationInfo.java b/core/src/proguard/optimize/info/ProgramMethodOptimizationInfo.java new file mode 100644 index 000000000..7b37a0eac --- /dev/null +++ b/core/src/proguard/optimize/info/ProgramMethodOptimizationInfo.java @@ -0,0 +1,588 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; +import proguard.util.ArrayUtil; + +/** + * This class stores some optimization information that can be attached to + * a method. + * + * @author Eric Lafortune + */ +public class ProgramMethodOptimizationInfo +extends MethodOptimizationInfo +{ + private static final Value[] EMPTY_PARAMETERS = new Value[0]; + + + private volatile boolean hasSideEffects = false; + private volatile boolean canBeMadePrivate = true; + private volatile boolean catchesExceptions = false; + private volatile boolean branchesBackward = false; + private volatile boolean invokesSuperMethods = false; + private volatile boolean invokesDynamically = false; + private volatile boolean accessesPrivateCode = false; + private volatile boolean accessesPackageCode = false; + private volatile boolean accessesProtectedCode = false; + private volatile boolean hasSynchronizedBlock = false; + private volatile boolean returnsWithNonEmptyStack = false; + private volatile int invocationCount = 0; + private volatile int parameterSize = 0; + private volatile long usedParameters = 0L; + private volatile long escapedParameters = 0L; + private volatile long escapingParameters = 0L; + private volatile long modifiedParameters = 0L; + private volatile boolean modifiesAnything = false; + private volatile Value[] parameters; + private volatile long returnedParameters = 0L; + private volatile boolean returnsNewInstances = false; + private volatile boolean returnsExternalValues = false; + private volatile Value returnValue; + + + /** + * Creates a new MethodOptimizationInfo for the given method. + */ + public ProgramMethodOptimizationInfo(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.ACC_STATIC) == 0) + { + parameterCount++; + } + + parameters = parameterCount == 0 ? + EMPTY_PARAMETERS : + new Value[parameterCount]; + } + + + public boolean isKept() + { + return false; + } + + + public void setSideEffects() + { + hasSideEffects = true; + } + + + public boolean hasSideEffects() + { + return !hasNoSideEffects && 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 setInvokesDynamically() + { + invokesDynamically = true; + } + + + public boolean invokesDynamically() + { + return invokesDynamically; + } + + + 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 setHasSynchronizedBlock() + { + hasSynchronizedBlock = true; + } + + + public boolean hasSynchronizedBlock() + { + return hasSynchronizedBlock; + } + + + public void setReturnsWithNonEmptyStack() + { + returnsWithNonEmptyStack = true; + } + + + public boolean returnsWithNonEmptyStack() + { + return returnsWithNonEmptyStack; + } + + + public void incrementInvocationCount() + { + invocationCount++; + } + + + public int getInvocationCount() + { + return invocationCount; + } + + + public synchronized void setParameterSize(int parameterSize) + { + this.parameterSize = parameterSize; + } + + + public int getParameterSize() + { + return parameterSize; + } + + + public synchronized void setParameterUsed(int variableIndex) + { + usedParameters = setBit(usedParameters, variableIndex); + } + + + public synchronized void updateUsedParameters(long usedParameters) + { + this.usedParameters |= usedParameters; + } + + + public boolean hasUnusedParameters() + { + return (usedParameters | -1L << parameterSize) != -1L; + } + + + public boolean isParameterUsed(int variableIndex) + { + return isBitSet(usedParameters, variableIndex); + } + + + public long getUsedParameters() + { + return usedParameters; + } + + + /** + * Notifies this object that a parameter is inserted at the given + * index. + * @param parameterIndex the parameter index, + * not taking into account the entry size, + * but taking into account the 'this' parameter, + * if any. + */ + public synchronized void insertParameter(int parameterIndex) + { + // The used parameter bits are indexed with their variable indices + // (which take into account the sizes of the entries). + //usedParameters = insertBit(usedParameters, parameterIndex, 1L); + //parameterSize++; + + escapedParameters = insertBit(escapedParameters, parameterIndex, 1L); + escapingParameters = insertBit(escapingParameters, parameterIndex, 1L); + modifiedParameters = insertBit(modifiedParameters, parameterIndex, 1L); + returnedParameters = insertBit(returnedParameters, parameterIndex, 1L); + parameters = ArrayUtil.insert(parameters, parameters.length, parameterIndex, null); + } + + + /** + * Notifies this object that the specified parameter is removed. + * @param parameterIndex the parameter index, + * not taking into account the entry size, + * but taking into account the 'this' parameter, + * if any. + */ + public synchronized void removeParameter(int parameterIndex) + { + // The used parameter bits are indexed with their variable indices + // (which take into account the sizes of the entries). + //usedParameters = removeBit(usedParameters, parameterIndex, 1L); + //parameterSize--; + + escapedParameters = removeBit(escapedParameters, parameterIndex, 1L); + escapingParameters = removeBit(escapingParameters, parameterIndex, 1L); + modifiedParameters = removeBit(modifiedParameters, parameterIndex, 1L); + returnedParameters = removeBit(returnedParameters, parameterIndex, 1L); + ArrayUtil.remove(parameters, parameters.length, parameterIndex); + } + + + public synchronized void setParameterEscaped(int parameterIndex) + { + escapedParameters = setBit(escapedParameters, parameterIndex); + } + + + public synchronized void updateEscapedParameters(long escapedParameters) + { + this.escapedParameters |= escapedParameters; + } + + + public boolean hasParameterEscaped(int parameterIndex) + { + return isBitSet(escapedParameters, parameterIndex); + } + + + public long getEscapedParameters() + { + return escapedParameters; + } + + + public synchronized void setParameterEscaping(int parameterIndex) + { + escapingParameters = setBit(escapingParameters, parameterIndex); + } + + + public synchronized void updateEscapingParameters(long escapingParameters) + { + this.escapingParameters |= escapingParameters; + } + + + public boolean isParameterEscaping(int parameterIndex) + { + return + !hasNoEscapingParameters && + (isBitSet(escapingParameters, parameterIndex)); + } + + + public long getEscapingParameters() + { + return hasNoEscapingParameters ? 0L : escapingParameters; + } + + + public synchronized void setParameterModified(int parameterIndex) + { + modifiedParameters = setBit(modifiedParameters, parameterIndex); + } + + + public synchronized void updateModifiedParameters(long modifiedParameters) + { + this.modifiedParameters |= modifiedParameters; + } + + + public boolean isParameterModified(int parameterIndex) + { + // TODO: Refine for static methods. + return + !hasNoSideEffects && + (!hasNoExternalSideEffects || parameterIndex == 0) && + (isBitSet((modifiesAnything ? + modifiedParameters | escapedParameters : + modifiedParameters), parameterIndex)); + } + + + public long getModifiedParameters() + { + // TODO: Refine for static methods. + return + hasNoSideEffects ? 0L : + hasNoExternalSideEffects ? modifiedParameters & 1L : + modifiedParameters; + } + + + public void setModifiesAnything() + { + modifiesAnything = true; + } + + + public boolean modifiesAnything() + { + return !hasNoExternalSideEffects && modifiesAnything; + } + + + public synchronized void generalizeParameterValue(int parameterIndex, Value parameter) + { + parameters[parameterIndex] = parameters[parameterIndex] != null ? + parameters[parameterIndex].generalize(parameter) : + parameter; + } + + + public Value getParameterValue(int parameterIndex) + { + return parameters != null ? + parameters[parameterIndex] : + null; + } + + + public synchronized void setParameterReturned(int parameterIndex) + { + returnedParameters = setBit(returnedParameters, parameterIndex); + } + + + public synchronized void updateReturnedParameters(long returnedParameters) + { + this.returnedParameters |= returnedParameters; + } + + + public boolean returnsParameter(int parameterIndex) + { + return isBitSet(returnedParameters, parameterIndex); + } + + + public long getReturnedParameters() + { + return returnedParameters; + } + + + public void setReturnsNewInstances() + { + returnsNewInstances = true; + } + + + public boolean returnsNewInstances() + { + return returnsNewInstances; + } + + + public void setReturnsExternalValues() + { + returnsExternalValues = true; + } + + + public boolean returnsExternalValues() + { + return + !hasNoExternalReturnValues && + returnsExternalValues; + } + + + public synchronized void generalizeReturnValue(Value returnValue) + { + this.returnValue = this.returnValue != null ? + this.returnValue.generalize(returnValue) : + returnValue; + } + + + public Value getReturnValue() + { + return returnValue; + } + + + // For setting enum return values. + public synchronized void setReturnValue(Value returnValue) + { + this.returnValue = returnValue; + } + + + public synchronized void merge(MethodOptimizationInfo other) + { + this.catchesExceptions |= other.catchesExceptions(); + this.branchesBackward |= other.branchesBackward(); + this.invokesSuperMethods |= other.invokesSuperMethods(); + this.invokesDynamically |= other.invokesDynamically(); + this.accessesPrivateCode |= other.accessesPrivateCode(); + this.accessesPackageCode |= other.accessesPackageCode(); + this.accessesProtectedCode |= other.accessesProtectedCode(); + this.hasSynchronizedBlock |= other.hasSynchronizedBlock(); + + // Some of these should actually be recomputed, since these are + // relative to the method: + // this.invokesSuperMethods + // this.accessesPrivateCode + // this.accessesPackageCode + // this.accessesProtectedCode + } + + + public static void setProgramMethodOptimizationInfo(Clazz clazz, Method method) + { + MethodLinker.lastMember(method).setVisitorInfo(new ProgramMethodOptimizationInfo(clazz, method)); + } + + + public static ProgramMethodOptimizationInfo getProgramMethodOptimizationInfo(Method method) + { + return (ProgramMethodOptimizationInfo)MethodLinker.lastMember(method).getVisitorInfo(); + } + + + // Small utility methods. + + /** + * Returns the given value with the specified bit set. + */ + private long setBit(long bits, int index) + { + return index < 64 ? + bits | (1L << index) : + bits; + } + + + /** + * Returns whether the specified bit is set in the given value + * (or if the index exceeds the size of the long). + */ + private boolean isBitSet(long bits, int index) + { + return index >= 64 || (bits & (1L << index)) != 0; + } + + + /** + * Returns the given value with a given bit inserted at the given index. + */ + private long insertBit(long value, int bitIndex, long bitValue) + { + long higherMask = -1L << bitIndex; + long lowerMask = ~higherMask; + + return ((value & higherMask) << 1) | + ( value & lowerMask ) | + (bitValue << bitIndex); + } + + + /** + * Returns the given value with a bit removed at the given index. + * The given given bit value is shifted in as the new most significant bit. + */ + private long removeBit(long value, int bitIndex, long highBitValue) + { + long higherMask = -1L << bitIndex; + long lowerMask = ~higherMask; + + return ((value & (higherMask<<1)) >>> 1) | + ( value & lowerMask ) | + (highBitValue << 63); + } +} diff --git a/src/proguard/optimize/info/ReadWriteFieldMarker.java b/core/src/proguard/optimize/info/ReadWriteFieldMarker.java similarity index 74% rename from src/proguard/optimize/info/ReadWriteFieldMarker.java rename to core/src/proguard/optimize/info/ReadWriteFieldMarker.java index 472309d3c..15a3a9d61 100644 --- a/src/proguard/optimize/info/ReadWriteFieldMarker.java +++ b/core/src/proguard/optimize/info/ReadWriteFieldMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -40,11 +40,32 @@ public class ReadWriteFieldMarker ConstantVisitor, MemberVisitor { + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("rwfm") != null; + //*/ + + + private final MutableBoolean repeatTrigger; + + // Parameters for the visitor methods. + // We'll set them to true by default, in case this class is being used + // as a field visitor. private boolean reading = true; private boolean writing = true; + /** + * Creates a new ReadWriteFieldMarker. + */ + public ReadWriteFieldMarker(MutableBoolean repeatTrigger) + { + this.repeatTrigger = repeatTrigger; + } + + // Implementations for InstructionVisitor. public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} @@ -126,38 +147,44 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie // Small utility methods. - private static void markAsRead(Field field) + private void markAsRead(Field field) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - if (info != null) + FieldOptimizationInfo fieldOptimizationInfo = + FieldOptimizationInfo.getFieldOptimizationInfo(field); + + if (!fieldOptimizationInfo.isRead() && + fieldOptimizationInfo instanceof ProgramFieldOptimizationInfo) { - info.setRead(); + ((ProgramFieldOptimizationInfo)fieldOptimizationInfo).setRead(); + + repeatTrigger.set(); } } public static boolean isRead(Field field) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - return info == null || - info.isRead(); + return FieldOptimizationInfo.getFieldOptimizationInfo(field).isRead(); } - private static void markAsWritten(Field field) + private void markAsWritten(Field field) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - if (info != null) + FieldOptimizationInfo fieldOptimizationInfo = + FieldOptimizationInfo.getFieldOptimizationInfo(field); + + if (!fieldOptimizationInfo.isWritten() && + fieldOptimizationInfo instanceof ProgramFieldOptimizationInfo) { - info.setWritten(); + ((ProgramFieldOptimizationInfo)fieldOptimizationInfo).setWritten(); + + repeatTrigger.set(); } } public static boolean isWritten(Field field) { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - return info == null || - info.isWritten(); + return FieldOptimizationInfo.getFieldOptimizationInfo(field).isWritten(); } } diff --git a/core/src/proguard/optimize/info/ReferenceEscapeChecker.java b/core/src/proguard/optimize/info/ReferenceEscapeChecker.java new file mode 100644 index 000000000..cd30c4363 --- /dev/null +++ b/core/src/proguard/optimize/info/ReferenceEscapeChecker.java @@ -0,0 +1,466 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.evaluation.*; +import proguard.util.ArrayUtil; + +/** + * This AttributeVisitor can tell whether reference parameters and instances + * are escaping, are modified, or are returned. + * + * @see ParameterEscapeMarker + * @author Eric Lafortune + */ +public class ReferenceEscapeChecker +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("rec") != null; + //*/ + + + private boolean[] instanceEscaping = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + private boolean[] instanceReturned = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + private boolean[] instanceModified = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + private boolean[] externalInstance = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; +// private boolean[] exceptionEscaping = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; +// private boolean[] exceptionReturned = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; +// private boolean[] exceptionModified = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + + private final PartialEvaluator partialEvaluator; + private final boolean runPartialEvaluator; + + // Parameters and values for visitor methods. + private Method referencingMethod; + private int referencingOffset; + private int referencingPopCount; + + + /** + * Creates a new ReferenceEscapeChecker. + */ + public ReferenceEscapeChecker() + { + this(new ReferenceTracingValueFactory(new BasicValueFactory())); + } + + + /** + * Creates a new ReferenceEscapeChecker. This private constructor gets around + * the constraint that it's not allowed to add statements before calling + * 'this'. + */ + private ReferenceEscapeChecker(ReferenceTracingValueFactory referenceTracingValueFactory) + { + this(new PartialEvaluator(referenceTracingValueFactory, + new ParameterTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory)), + true, + referenceTracingValueFactory), + true); + } + + + /** + * Creates a new ReferenceEscapeChecker. + * @param partialEvaluator the evaluator to be used for the analysis. + * @param runPartialEvaluator specifies whether to run this evaluator on + * every code attribute that is visited. + */ + public ReferenceEscapeChecker(PartialEvaluator partialEvaluator, + boolean runPartialEvaluator) + { + this.partialEvaluator = partialEvaluator; + this.runPartialEvaluator = runPartialEvaluator; + } + + + /** + * Returns whether the instance created or retrieved at the specified + * instruction offset is escaping. + */ + public boolean isInstanceEscaping(int instructionOffset) + { + return instanceEscaping[instructionOffset]; + } + + + /** + * Returns whether the instance created or retrieved at the specified + * instruction offset is being returned. + */ + public boolean isInstanceReturned(int instructionOffset) + { + return instanceReturned[instructionOffset]; + } + + + /** + * Returns whether the instance created or retrieved at the specified + * instruction offset is being modified. + */ + public boolean isInstanceModified(int instructionOffset) + { + return instanceModified[instructionOffset]; + } + + + /** + * Returns whether the instance created or retrieved at the specified + * instruction offset is external to this method and its invoked methods. + */ + public boolean isInstanceExternal(int instructionOffset) + { + return externalInstance[instructionOffset]; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Evaluate the method. + if (runPartialEvaluator) + { + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + } + + int codeLength = codeAttribute.u4codeLength; + + // Initialize the global arrays. + instanceEscaping = ArrayUtil.ensureArraySize(instanceEscaping, codeLength, false); + instanceReturned = ArrayUtil.ensureArraySize(instanceReturned, codeLength, false); + instanceModified = ArrayUtil.ensureArraySize(instanceModified, codeLength, false); + externalInstance = ArrayUtil.ensureArraySize(externalInstance, codeLength, false); + + // Mark the parameters and instances that are escaping from the code. + codeAttribute.instructionsAccept(clazz, method, partialEvaluator.tracedInstructionFilter(this)); + + if (DEBUG) + { + System.out.println(); + System.out.println("ReferenceEscapeChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + + for (int index = 0; index < codeLength; index++) + { + if (partialEvaluator.isInstruction(index)) + { + System.out.println(" " + + (instanceEscaping[index] ? 'E' : '.') + + (instanceReturned[index] ? 'R' : '.') + + (instanceModified[index] ? 'M' : '.') + + (externalInstance[index] ? 'X' : '.') + + ' ' + + InstructionFactory.create(codeAttribute.code, index).toString(index)); + } + } + } + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + case InstructionConstants.OP_AASTORE: + // Mark array reference values whose element is modified. + markModifiedReferenceValues(offset, + simpleInstruction.stackPopCount(clazz) - 1); + + // Mark reference values that are put in the array. + markEscapingReferenceValues(offset, 0); + break; + + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + // Mark array reference values whose element is modified. + markModifiedReferenceValues(offset, + simpleInstruction.stackPopCount(clazz) - 1); + break; + + case InstructionConstants.OP_ARETURN: + // Mark the returned reference values. + markReturnedReferenceValues(offset, 0); + break; + + case InstructionConstants.OP_ATHROW: + // Mark the escaping reference values. + markEscapingReferenceValues(offset, 0); + 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: + // Mark external reference values. + markExternalReferenceValue(offset); + break; + + case InstructionConstants.OP_PUTSTATIC: + // Mark reference values that are put in the field. + markEscapingReferenceValues(offset, 0); + break; + + case InstructionConstants.OP_PUTFIELD: + // Mark reference reference values whose field is modified. + markModifiedReferenceValues(offset, + constantInstruction.stackPopCount(clazz) - 1); + + // Mark reference values that are put in the field. + markEscapingReferenceValues(offset, 0); + break; + + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + // Mark reference reference values that are modified as parameters + // of the invoked method. + // Mark reference values that are escaping as parameters + // of the invoked method. + // Mark escaped reference reference values in the invoked method. + referencingMethod = method; + referencingOffset = offset; + referencingPopCount = constantInstruction.stackPopCount(clazz); + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + clazz.constantPoolEntryAccept(fieldrefConstant.u2classIndex, this); + } + + + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + Method referencedMethod = (Method)refConstant.referencedMember; + + // Mark reference reference values that are passed to the method. + for (int index = 0; index < referencingPopCount; index++) + { + int stackEntryIndex = referencingPopCount - index - 1; + + TracedStack stackBefore = partialEvaluator.getStackBefore(referencingOffset); + Value stackEntry = stackBefore.getTop(stackEntryIndex); + + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + // Is the parameter escaping from the referenced method? + if (referencedMethod == null || + ParameterEscapeMarker.isParameterEscaping(referencedMethod, index)) + { + markEscapingReferenceValues(referencingOffset, + stackEntryIndex); + } + + // Is the parameter being modified in the referenced method? + if (referencedMethod == null || + ParameterEscapeMarker.isParameterModified(referencedMethod, index)) + { + markModifiedReferenceValues(referencingOffset, + stackEntryIndex); + } + } + } + + // Is the return value from the referenced method external? + String returnType = + ClassUtil.internalMethodReturnType(refConstant.getType(clazz)); + + if (referencedMethod == null || + ((ClassUtil.isInternalClassType(returnType) || + ClassUtil.isInternalArrayType(returnType)) && + ParameterEscapeMarker.returnsExternalValues(referencedMethod))) + { + markExternalReferenceValue(referencingOffset); + } + } + + + // Small utility methods. + + /** + * Marks the producing offsets of the specified stack entry at the given + * instruction offset. + */ + private void markEscapingReferenceValues(int instructionOffset, + int stackEntryIndex) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(instructionOffset); + Value stackEntry = stackBefore.getTop(stackEntryIndex); + + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + ReferenceValue referenceValue = stackEntry.referenceValue(); + + // The null reference value may not have a trace value. + if (referenceValue.isNull() != Value.ALWAYS) + { + markEscapingReferenceValues(referenceValue); + } + } + } + + + /** + * Marks the producing offsets of the given traced reference value. + */ + private void markEscapingReferenceValues(ReferenceValue referenceValue) + { + TracedReferenceValue tracedReferenceValue = (TracedReferenceValue)referenceValue; + InstructionOffsetValue instructionOffsetValue = tracedReferenceValue.getTraceValue().instructionOffsetValue(); + + int parameterCount = instructionOffsetValue.instructionOffsetCount(); + for (int index = 0; index < parameterCount; index++) + { + if (!instructionOffsetValue.isMethodParameter(index)) + { + instanceEscaping[instructionOffsetValue.instructionOffset(index)] = true; + } + } + } + + + /** + * Marks the producing offsets of the specified stack entry at the given + * instruction offset. + */ + private void markReturnedReferenceValues(int instructionOffset, + int stackEntryIndex) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(instructionOffset); + ReferenceValue referenceValue = stackBefore.getTop(stackEntryIndex).referenceValue(); + + // The null reference value may not have a trace value. + if (referenceValue.isNull() != Value.ALWAYS) + { + markReturnedReferenceValues(referenceValue); + } + } + + + /** + * Marks the producing offsets of the given traced reference value. + */ + private void markReturnedReferenceValues(ReferenceValue referenceValue) + { + TracedReferenceValue tracedReferenceValue = (TracedReferenceValue)referenceValue; + InstructionOffsetValue instructionOffsetValue = tracedReferenceValue.getTraceValue().instructionOffsetValue(); + + int parameterCount = instructionOffsetValue.instructionOffsetCount(); + for (int index = 0; index < parameterCount; index++) + { + if (!instructionOffsetValue.isMethodParameter(index)) + { + instanceReturned[instructionOffsetValue.instructionOffset(index)] = true; + } + } + } + + + /** + * Marks the producing offsets of the specified stack entry at the given + * instruction offset. + */ + private void markModifiedReferenceValues(int instructionOffset, + int stackEntryIndex) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(instructionOffset); + ReferenceValue referenceValue = stackBefore.getTop(stackEntryIndex).referenceValue(); + + // The null reference value may not have a trace value. + if (referenceValue.isNull() != Value.ALWAYS) + { + markModifiedReferenceValues(referenceValue); + } + } + + + /** + * Marks the producing offsets of the given traced reference value. + */ + private void markModifiedReferenceValues(ReferenceValue referenceValue) + { + TracedReferenceValue tracedReferenceValue = (TracedReferenceValue)referenceValue; + InstructionOffsetValue instructionOffsetValue = tracedReferenceValue.getTraceValue().instructionOffsetValue(); + + int parameterCount = instructionOffsetValue.instructionOffsetCount(); + for (int index = 0; index < parameterCount; index++) + { + if (!instructionOffsetValue.isMethodParameter(index)) + { + instanceModified[instructionOffsetValue.instructionOffset(index)] = true; + } + } + } + + + /** + * Marks the producing offsets of the specified stack entry at the given + * instruction offset. + */ + private void markExternalReferenceValue(int offset) + { + externalInstance[offset] = true; + } +} \ No newline at end of file diff --git a/core/src/proguard/optimize/info/RepeatedClassPoolVisitor.java b/core/src/proguard/optimize/info/RepeatedClassPoolVisitor.java new file mode 100644 index 000000000..6dd48185a --- /dev/null +++ b/core/src/proguard/optimize/info/RepeatedClassPoolVisitor.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.ClassPool; +import proguard.classfile.visitor.ClassPoolVisitor; + +/** + * This ClassPoolVisitor repeatedly delegates to a given class pool visitor, as + * long as it keeps setting a given flag. + * + * @author Eric Lafortune + */ +public class RepeatedClassPoolVisitor +implements ClassPoolVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("rcpv") != null; + //*/ + + + private final MutableBoolean repeatTrigger; + private final ClassPoolVisitor classPoolVisitor; + + + /** + * Creates a new RepeatedClassPoolVisitor. + * @param repeatTrigger the mutable boolean flag that the class pool + * visitor can set to indicate that the class pool + * should be visited again. + * @param classPoolVisitor the class pool visitor to apply. + */ + public RepeatedClassPoolVisitor(MutableBoolean repeatTrigger, + ClassPoolVisitor classPoolVisitor) + { + this.repeatTrigger = repeatTrigger; + this.classPoolVisitor = classPoolVisitor; + } + + + // Implementations for ClassPoolVisitor. + + public void visitClassPool(ClassPool classPool) + { + // Visit all classes at least once, until the class visitors stop + // setting the repeat trigger. + do + { + if (DEBUG) + { + System.out.println("RepeatedClassPoolVisitor: new iteration"); + } + + repeatTrigger.reset(); + + // Visit over all classes once. + classPoolVisitor.visitClassPool(classPool); + } + while (repeatTrigger.isSet()); + + if (DEBUG) + { + System.out.println("RepeatedClassPoolVisitor: done iterating"); + } + } +} diff --git a/core/src/proguard/optimize/info/SideEffectClassChecker.java b/core/src/proguard/optimize/info/SideEffectClassChecker.java new file mode 100644 index 000000000..44e985b8a --- /dev/null +++ b/core/src/proguard/optimize/info/SideEffectClassChecker.java @@ -0,0 +1,84 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.ClassCollector; + +import java.util.*; + +/** + * This utility class contains methods to check whether referencing classes + * may have side effects due to them being loaded and initialized. + * + * @see NoSideEffectClassMarker + * @see SideEffectClassMarker + * @author Eric Lafortune + */ +public class SideEffectClassChecker +{ + /** + * Returns whether accessing the given class member from the given class may + * have side effects when they are initialized. + */ + public static boolean mayHaveSideEffects(Clazz referencingClass, + Clazz referencedClass, + Member referencedMember) + { + // Is the referenced class member static or an initializer method? + // Does accessing the referenced class then have side effects? + return + ((referencedMember.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 || + referencedMember.getName(referencedClass).equals(ClassConstants.METHOD_NAME_INIT)) && + mayHaveSideEffects(referencingClass, referencedClass); + } + + + /** + * Returns whether accessing the given class from another given class may + * have side effects when they are initialized. + */ + public static boolean mayHaveSideEffects(Clazz referencingClass, + Clazz referencedClass) + { + return + !NoSideEffectClassMarker.hasNoSideEffects(referencedClass) && + !referencingClass.extendsOrImplements(referencedClass) && + !sideEffectSuperClasses(referencingClass).containsAll(sideEffectSuperClasses(referencedClass)); + } + + + /** + * Returns the set of superclasses and interfaces that are initialized. + */ + private static Set sideEffectSuperClasses(Clazz clazz) + { + Set set = new HashSet(); + + // Visit all superclasses and interfaces, collecting the ones that have + // side effects when they are initialized. + clazz.hierarchyAccept(true, true, true, false, + new SideEffectClassFilter( + new ClassCollector(set))); + + return set; + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java b/core/src/proguard/optimize/info/SideEffectClassFilter.java similarity index 78% rename from src/proguard/optimize/info/StaticInitializerContainingClassFilter.java rename to core/src/proguard/optimize/info/SideEffectClassFilter.java index 0000b7d76..ab5e2b82a 100644 --- a/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java +++ b/core/src/proguard/optimize/info/SideEffectClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,17 +25,17 @@ /** * This ClassVisitor delegates all its method calls to another ClassVisitor, - * but only for Clazz objects that are instantiated. + * but only for Clazz objects that have side effects when they are initialized. * * @author Eric Lafortune */ -public class StaticInitializerContainingClassFilter +public class SideEffectClassFilter implements ClassVisitor { private final ClassVisitor classVisitor; - public StaticInitializerContainingClassFilter(ClassVisitor classVisitor) + public SideEffectClassFilter(ClassVisitor classVisitor) { this.classVisitor = classVisitor; } @@ -45,7 +45,7 @@ public StaticInitializerContainingClassFilter(ClassVisitor classVisitor) public void visitProgramClass(ProgramClass programClass) { - if (StaticInitializerContainingClassMarker.containsStaticInitializer(programClass)) + if (SideEffectClassMarker.hasSideEffects(programClass)) { classVisitor.visitProgramClass(programClass); } @@ -54,7 +54,7 @@ public void visitProgramClass(ProgramClass programClass) public void visitLibraryClass(LibraryClass libraryClass) { - if (StaticInitializerContainingClassMarker.containsStaticInitializer(libraryClass)) + if (SideEffectClassMarker.hasSideEffects(libraryClass)) { classVisitor.visitLibraryClass(libraryClass); } diff --git a/src/proguard/optimize/info/ClassOptimizationInfoSetter.java b/core/src/proguard/optimize/info/SideEffectClassMarker.java similarity index 62% rename from src/proguard/optimize/info/ClassOptimizationInfoSetter.java rename to core/src/proguard/optimize/info/SideEffectClassMarker.java index ea143d68b..46d7e13c8 100644 --- a/src/proguard/optimize/info/ClassOptimizationInfoSetter.java +++ b/core/src/proguard/optimize/info/SideEffectClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,28 +20,37 @@ */ package proguard.optimize.info; -import proguard.classfile.ProgramClass; +import proguard.classfile.*; import proguard.classfile.util.SimplifiedVisitor; -import proguard.classfile.visitor.ClassVisitor; -import proguard.optimize.KeepMarker; +import proguard.classfile.visitor.*; /** - * This ClassVisitor attaches a ClassOptimizationInfo instance to every class - * that is not being kept that it visits. + * This ClassVisitor marks all classes that it visits as having side effects. * * @author Eric Lafortune */ -public class ClassOptimizationInfoSetter +public class SideEffectClassMarker extends SimplifiedVisitor implements ClassVisitor { - // Implementations for MemberVisitor. + // Implementations for ClassVisitor. public void visitProgramClass(ProgramClass programClass) { - if (!KeepMarker.isKept(programClass)) - { - ClassOptimizationInfo.setClassOptimizationInfo(programClass); - } + markSideEffects(programClass); + } + + + // Small utility methods. + + private static void markSideEffects(Clazz clazz) + { + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setSideEffects(); + } + + + public static boolean hasSideEffects(Clazz clazz) + { + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).hasSideEffects(); } } \ No newline at end of file diff --git a/src/proguard/optimize/info/SideEffectInstructionChecker.java b/core/src/proguard/optimize/info/SideEffectInstructionChecker.java similarity index 76% rename from src/proguard/optimize/info/SideEffectInstructionChecker.java rename to core/src/proguard/optimize/info/SideEffectInstructionChecker.java index 5374c4e0c..8dd4602e0 100644 --- a/src/proguard/optimize/info/SideEffectInstructionChecker.java +++ b/core/src/proguard/optimize/info/SideEffectInstructionChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,17 +27,14 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; -import proguard.classfile.visitor.*; - -import java.util.*; +import proguard.classfile.visitor.MemberVisitor; /** - * This class can tell whether an instruction has any side effects outside of - * its method. Return instructions and local field accesses can be included or - * not. + * This class can tell whether an instruction has any side effects. Return + * instructions and array store instructions can be included or not. * * @see ReadWriteFieldMarker - * @see StaticInitializerContainingClassMarker + * @see SideEffectClassMarker * @see NoSideEffectMethodMarker * @see SideEffectMethodMarker * @author Eric Lafortune @@ -48,13 +45,13 @@ public class SideEffectInstructionChecker ConstantVisitor, MemberVisitor { - static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null; + public static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null; private final boolean includeReturnInstructions; - private final boolean includeLocalFieldAccess; + private final boolean includeArrayStoreInstructions; - // A return value for the visitor methods. + // Parameters and return values for the visitor methods. private boolean writingField; private Clazz referencingClass; private boolean hasSideEffects; @@ -62,28 +59,21 @@ public class SideEffectInstructionChecker /** * Creates a new SideEffectInstructionChecker - * @param includeReturnInstructions specifies whether return instructions - * count as side effects. - * @param includeLocalFieldAccess specifies whether reading or writing - * local fields counts as side effects. + * @param includeReturnInstructions specifies whether return + * instructions count as side + * effects. + * @param includeArrayStoreInstructions specifies whether storing values + * in arrays counts as side effects. */ public SideEffectInstructionChecker(boolean includeReturnInstructions, - boolean includeLocalFieldAccess) + boolean includeArrayStoreInstructions) { - this.includeReturnInstructions = includeReturnInstructions; - this.includeLocalFieldAccess = includeLocalFieldAccess; + this.includeReturnInstructions = includeReturnInstructions; + this.includeArrayStoreInstructions = includeArrayStoreInstructions; } - /** - * 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) + public boolean hasSideEffects(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) { hasSideEffects = false; @@ -109,6 +99,10 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod case InstructionConstants.OP_LDIV: case InstructionConstants.OP_IREM: case InstructionConstants.OP_LREM: + case InstructionConstants.OP_FDIV: + case InstructionConstants.OP_FREM: + case InstructionConstants.OP_DDIV: + case InstructionConstants.OP_DREM: case InstructionConstants.OP_IALOAD: case InstructionConstants.OP_LALOAD: case InstructionConstants.OP_FALOAD: @@ -119,8 +113,6 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod case InstructionConstants.OP_SALOAD: case InstructionConstants.OP_NEWARRAY: case InstructionConstants.OP_ARRAYLENGTH: - case InstructionConstants.OP_ANEWARRAY: - case InstructionConstants.OP_MULTIANEWARRAY: // These instructions strictly taken may cause a side effect // (ArithmeticException, NullPointerException, // ArrayIndexOutOfBoundsException, NegativeArraySizeException). @@ -135,6 +127,10 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod case InstructionConstants.OP_BASTORE: case InstructionConstants.OP_CASTORE: case InstructionConstants.OP_SASTORE: + // These instructions may cause a side effect. + hasSideEffects = includeArrayStoreInstructions; + break; + case InstructionConstants.OP_ATHROW : case InstructionConstants.OP_MONITORENTER: case InstructionConstants.OP_MONITOREXIT: @@ -242,8 +238,8 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c break; case InstructionConstants.OP_ANEWARRAY: - case InstructionConstants.OP_CHECKCAST: case InstructionConstants.OP_MULTIANEWARRAY: + case InstructionConstants.OP_CHECKCAST: // This instructions strictly taken may cause a side effect // (ClassCastException, NegativeArraySizeException). hasSideEffects = OPTIMIZE_CONSERVATIVELY; @@ -307,10 +303,11 @@ public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) public void visitProgramField(ProgramClass programClass, ProgramField programField) { hasSideEffects = - (includeLocalFieldAccess || !programClass.equals(referencingClass)) && - ((writingField && ReadWriteFieldMarker.isRead(programField)) || - (programField.getAccessFlags() & ClassConstants.ACC_VOLATILE) != 0 || - mayHaveSideEffects(referencingClass, programClass)); + (writingField && ReadWriteFieldMarker.isRead(programField)) || + (programField.getAccessFlags() & ClassConstants.ACC_VOLATILE) != 0 || + SideEffectClassChecker.mayHaveSideEffects(referencingClass, + programClass, + programField); } @@ -319,9 +316,10 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM // Note that side effects already include synchronization of some // implementation of the method. hasSideEffects = - !NoSideEffectMethodMarker.hasNoSideEffects(programMethod) && - (SideEffectMethodMarker.hasSideEffects(programMethod) || - mayHaveSideEffects(referencingClass, programClass)); + SideEffectMethodMarker.hasSideEffects(programMethod) || + SideEffectClassChecker.mayHaveSideEffects(referencingClass, + programClass, + programMethod); } @@ -336,40 +334,4 @@ public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryM hasSideEffects = !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod); } - - - // Small utility methods. - - /** - * Returns whether a field reference or method invocation from the - * referencing class to the referenced class might have any side - * effects. - */ - private boolean mayHaveSideEffects(Clazz referencingClass, Clazz referencedClass) - { - return - !referencedClass.equals(referencingClass) && - !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(referencedClass)); - } - - - /** - * 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.METHOD_NAME_CLINIT, - ClassConstants.METHOD_TYPE_CLINIT, - new SideEffectMethodFilter( - new MemberToClassVisitor( - new ClassCollector(set)))))); - - return set; - } } diff --git a/src/proguard/optimize/info/SideEffectMethodFilter.java b/core/src/proguard/optimize/info/SideEffectMethodFilter.java similarity index 97% rename from src/proguard/optimize/info/SideEffectMethodFilter.java rename to core/src/proguard/optimize/info/SideEffectMethodFilter.java index 2e3824537..dd0765b22 100644 --- a/src/proguard/optimize/info/SideEffectMethodFilter.java +++ b/core/src/proguard/optimize/info/SideEffectMethodFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/optimize/info/SideEffectMethodMarker.java b/core/src/proguard/optimize/info/SideEffectMethodMarker.java new file mode 100644 index 000000000..a94a62e2d --- /dev/null +++ b/core/src/proguard/optimize/info/SideEffectMethodMarker.java @@ -0,0 +1,120 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.Instruction; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; +import proguard.optimize.OptimizationInfoClassFilter; + +/** + * This MemberVisitor and InstructionVisitor marks all methods and classes + * that have side effects. + * + * @see NoSideEffectMethodMarker + * @author Eric Lafortune + */ +public class SideEffectMethodMarker +extends SimplifiedVisitor +implements MemberVisitor, + InstructionVisitor +{ + private final MutableBoolean repeatTrigger; + + private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(false, true); + private final ClassVisitor sideEffectClassMarker = new OptimizationInfoClassFilter( + new SideEffectClassMarker()); + + + + /** + * Creates a new SideEffectMethodMarker. + */ + public SideEffectMethodMarker(MutableBoolean repeatTrigger) + { + this.repeatTrigger = repeatTrigger; + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if ((programMethod.getAccessFlags() & + (ClassConstants.ACC_NATIVE | + ClassConstants.ACC_SYNCHRONIZED)) != 0) + { + markSideEffects(programClass, programMethod); + } + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Check if it may be throwing exceptions. + if (sideEffectInstructionChecker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + instruction)) + { + markSideEffects(clazz, method); + } + } + + + // Small utility methods. + + private void markSideEffects(Clazz clazz, Method method) + { + MethodOptimizationInfo methodOptimizationInfo = + MethodOptimizationInfo.getMethodOptimizationInfo(method); + + if (!methodOptimizationInfo.hasSideEffects() && + methodOptimizationInfo instanceof ProgramMethodOptimizationInfo) + { + ((ProgramMethodOptimizationInfo)methodOptimizationInfo).setSideEffects(); + + // Trigger the repeater if the setter has changed the value. + if (methodOptimizationInfo.hasSideEffects()) + { + repeatTrigger.set(); + + // Also mark the class if the method is a static initializer. + if (method.getName(clazz).equals(ClassConstants.METHOD_NAME_CLINIT)) + { + clazz.accept(sideEffectClassMarker); + } + } + } + } + + + public static boolean hasSideEffects(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).hasSideEffects(); + } +} diff --git a/src/proguard/optimize/info/SimpleEnumFilter.java b/core/src/proguard/optimize/info/SimpleEnumFilter.java similarity index 98% rename from src/proguard/optimize/info/SimpleEnumFilter.java rename to core/src/proguard/optimize/info/SimpleEnumFilter.java index 9d05a9f2e..b076b31c1 100644 --- a/src/proguard/optimize/info/SimpleEnumFilter.java +++ b/core/src/proguard/optimize/info/SimpleEnumFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/SimpleEnumMarker.java b/core/src/proguard/optimize/info/SimpleEnumMarker.java similarity index 82% rename from src/proguard/optimize/info/SimpleEnumMarker.java rename to core/src/proguard/optimize/info/SimpleEnumMarker.java index 6877f37ae..bd4f4bac0 100644 --- a/src/proguard/optimize/info/SimpleEnumMarker.java +++ b/core/src/proguard/optimize/info/SimpleEnumMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -59,17 +59,12 @@ public void visitProgramClass(ProgramClass programClass) private void setSimpleEnum(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setSimpleEnum(simple); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setSimpleEnum(simple); } public static boolean isSimpleEnum(Clazz clazz) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - return info != null && info.isSimpleEnum(); + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).isSimpleEnum(); } } \ No newline at end of file diff --git a/src/proguard/optimize/info/SuperInvocationMarker.java b/core/src/proguard/optimize/info/SuperInvocationMarker.java similarity index 87% rename from src/proguard/optimize/info/SuperInvocationMarker.java rename to core/src/proguard/optimize/info/SuperInvocationMarker.java index 63e622550..e3a670af1 100644 --- a/src/proguard/optimize/info/SuperInvocationMarker.java +++ b/core/src/proguard/optimize/info/SuperInvocationMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -77,17 +77,12 @@ public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) private static void setInvokesSuperMethods(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setInvokesSuperMethods(); - } + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setInvokesSuperMethods(); } public static boolean invokesSuperMethods(Method method) { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info == null || info.invokesSuperMethods(); + return MethodOptimizationInfo.getMethodOptimizationInfo(method).invokesSuperMethods(); } } diff --git a/core/src/proguard/optimize/info/SynchronizedBlockMethodMarker.java b/core/src/proguard/optimize/info/SynchronizedBlockMethodMarker.java new file mode 100644 index 000000000..963843e8a --- /dev/null +++ b/core/src/proguard/optimize/info/SynchronizedBlockMethodMarker.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 the existence of synchronized blocks + * of the methods whose instructions it visits. + * + * @author Thomas Neidhart + */ +public class SynchronizedBlockMethodMarker +extends SimplifiedVisitor +implements InstructionVisitor +{ + // 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) + { + if (simpleInstruction.opcode == InstructionConstants.OP_MONITORENTER || + simpleInstruction.opcode == InstructionConstants.OP_MONITOREXIT) + { + setHasSynchronizedBlock(method); + } + } + + // Small utility methods. + + private static void setHasSynchronizedBlock(Method method) + { + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method).setHasSynchronizedBlock(); + } + + + /** + * Returns whether the given method accesses private class members. + */ + public static boolean hasSynchronizedBlock(Method method) + { + return MethodOptimizationInfo.getMethodOptimizationInfo(method).hasSynchronizedBlock(); + } + +} diff --git a/src/proguard/optimize/info/MemberOptimizationInfoSetter.java b/core/src/proguard/optimize/info/UnusedParameterMethodFilter.java similarity index 56% rename from src/proguard/optimize/info/MemberOptimizationInfoSetter.java rename to core/src/proguard/optimize/info/UnusedParameterMethodFilter.java index 4a09e09ed..47f500f37 100644 --- a/src/proguard/optimize/info/MemberOptimizationInfoSetter.java +++ b/core/src/proguard/optimize/info/UnusedParameterMethodFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,39 +21,45 @@ 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. + * This MemberVisitor delegates all its method calls to another MemberVisitor, + * but only for Method objects that are marked as having unused parameters. + * + * @see ParameterUsageMarker * * @author Eric Lafortune */ -public class MemberOptimizationInfoSetter -extends SimplifiedVisitor +public class UnusedParameterMethodFilter implements MemberVisitor { - // Implementations for MemberVisitor. + private final MemberVisitor memberVisitor; + - public void visitProgramField(ProgramClass programClass, ProgramField programField) + /** + * Creates a new UnusedParameterMethodFilter. + * @param memberVisitor the member visitor to which the visiting will be + * delegated. + */ + public UnusedParameterMethodFilter(MemberVisitor memberVisitor) { - if (!KeepMarker.isKept(programField)) - { - FieldOptimizationInfo.setFieldOptimizationInfo(programClass, - programField); - } + this.memberVisitor = memberVisitor; } + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) {} + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - if (!KeepMarker.isKept(programMethod)) + if (ParameterUsageMarker.hasUnusedParameters(programMethod)) { - MethodOptimizationInfo.setMethodOptimizationInfo(programClass, - programMethod); + memberVisitor.visitProgramMethod(programClass, programMethod); } } -} +} \ No newline at end of file diff --git a/core/src/proguard/optimize/info/UnusedParameterOptimizationInfoUpdater.java b/core/src/proguard/optimize/info/UnusedParameterOptimizationInfoUpdater.java new file mode 100644 index 000000000..6432d5602 --- /dev/null +++ b/core/src/proguard/optimize/info/UnusedParameterOptimizationInfoUpdater.java @@ -0,0 +1,140 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; +import proguard.classfile.visitor.*; +import proguard.optimize.*; + +/** + * This AttributeVisitor removes unused parameters from the optimization info + * of the methods that it visits. This includes 'this' parameters. + * + * @see ParameterUsageMarker + * @see MethodStaticizer + * @see MethodDescriptorShrinker + * @author Eric Lafortune + */ +public class UnusedParameterOptimizationInfoUpdater +extends SimplifiedVisitor +implements AttributeVisitor, + + // Internal implementations. + ParameterVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("upoiu") != null; + //*/ + + + private final MemberVisitor extraUnusedParameterMethodVisitor; + + private final MemberVisitor unusedParameterRemover = new AllParameterVisitor(true, + new UsedParameterFilter(null, this)); + + // Parameters and return values for visitor methods. + private int removedParameterSize; + private int removedParameterCount; + + + /** + * Creates a new UnusedParameterOptimizationInfoUpdater. + */ + public UnusedParameterOptimizationInfoUpdater() + { + this(null); + } + + + /** + * Creates a new UnusedParameterOptimizationInfoUpdater with an extra + * visitor. + * @param extraUnusedParameterMethodVisitor an optional extra visitor for + * all removed parameters. + */ + public UnusedParameterOptimizationInfoUpdater(MemberVisitor extraUnusedParameterMethodVisitor) + { + this.extraUnusedParameterMethodVisitor = extraUnusedParameterMethodVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println("UnusedParameterOptimizationInfoUpdater: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + } + + // Update the optimization info. + removedParameterCount = 0; + removedParameterSize = 0; + + method.accept(clazz, unusedParameterRemover); + + // Compute the new parameter size from the shrunk descriptor. + ProgramMethodOptimizationInfo programMethodOptimizationInfo = + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method); + + int newParameterSize = + programMethodOptimizationInfo.getParameterSize() - removedParameterSize; + + programMethodOptimizationInfo.setParameterSize(newParameterSize); + programMethodOptimizationInfo.updateUsedParameters(-1L); + } + + + // Implementations for ParameterVisitor. + + public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) + { + if (DEBUG) + { + System.out.println(" Deleting parameter #"+parameterIndex+" (v"+parameterOffset+")"); + } + + Method method = (Method)member; + + // Remove the unused parameter in the optimization info. + // Take into acount the delta from previously removed parameters. + ProgramMethodOptimizationInfo programMethodOptimizationInfo = + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(method); + + programMethodOptimizationInfo.removeParameter(parameterIndex - removedParameterCount++); + + removedParameterSize += ClassUtil.internalTypeSize(parameterType); + + // Visit the method, if required. + if (extraUnusedParameterMethodVisitor != null) + { + method.accept(clazz, extraUnusedParameterMethodVisitor); + } + } +} diff --git a/core/src/proguard/optimize/info/UsedParameterFilter.java b/core/src/proguard/optimize/info/UsedParameterFilter.java new file mode 100644 index 000000000..c3f293f04 --- /dev/null +++ b/core/src/proguard/optimize/info/UsedParameterFilter.java @@ -0,0 +1,85 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; + +/** + * This ParameterVisitor delegates all its visits to one of two other + * ParameterVisitor instances, depending on whether the parameter is + * used or not. + * + * @see ParameterUsageMarker + * @author Eric Lafortune + */ +public class UsedParameterFilter +implements ParameterVisitor +{ + private final ParameterVisitor usedParameterVisitor; + private final ParameterVisitor unusedParameterVisitor; + + + /** + * Creates a new UsedParameterFilter that delegates visits to used + * parameters to the given parameter visitor. + */ + public UsedParameterFilter(ParameterVisitor usedParameterVisitor) + { + this(usedParameterVisitor, null); + } + + + /** + * Creates a new UsedParameterFilter that delegates to one of the two + * given parameter visitors. + */ + public UsedParameterFilter(ParameterVisitor usedParameterVisitor, + ParameterVisitor unusedParameterVisitor) + { + this.usedParameterVisitor = usedParameterVisitor; + this.unusedParameterVisitor = unusedParameterVisitor; + } + + + // Implementations for ParameterVisitor. + + public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) + { + ParameterVisitor parameterVisitor = + ParameterUsageMarker.isParameterUsed((Method)member, + parameterOffset) ? + usedParameterVisitor : + unusedParameterVisitor; + + if (parameterVisitor != null) + { + parameterVisitor.visitParameter(clazz, + member, + parameterIndex, + parameterCount, + parameterOffset, + parameterSize, + parameterType, + referencedClass); + } + } +} \ No newline at end of file diff --git a/src/proguard/optimize/info/VariableUsageMarker.java b/core/src/proguard/optimize/info/VariableUsageMarker.java similarity index 94% rename from src/proguard/optimize/info/VariableUsageMarker.java rename to core/src/proguard/optimize/info/VariableUsageMarker.java index ba31ac487..11cf2744b 100644 --- a/src/proguard/optimize/info/VariableUsageMarker.java +++ b/core/src/proguard/optimize/info/VariableUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -88,7 +88,8 @@ public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute c variableUsed[variableInstruction.variableIndex] = true; // Account for Category 2 instructions, which take up two entries. - if (variableInstruction.isCategory2()) + if (variableInstruction.stackPopCount(clazz) == 2 || + variableInstruction.stackPushCount(clazz) == 2) { variableUsed[variableInstruction.variableIndex + 1] = true; } diff --git a/core/src/proguard/optimize/info/WrapperClassMarker.java b/core/src/proguard/optimize/info/WrapperClassMarker.java new file mode 100644 index 000000000..aebb682d6 --- /dev/null +++ b/core/src/proguard/optimize/info/WrapperClassMarker.java @@ -0,0 +1,225 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.constant.*; +import proguard.classfile.instruction.*; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.evaluation.value.*; +import proguard.optimize.evaluation.StoringInvocationUnit; + +/** + * This ClassVisitor marks all program classes that are a simple wrapper for a + * single non-null instance of another class. + * + * A wrapper class has + * - exactly one non-static field, which references an object, + * - exactly one initializer, with a single parameter that is never null, + * that initializes the field, + * - no subclasses. + * + * @see StoringInvocationUnit + * @author Eric Lafortune + */ +public class WrapperClassMarker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + public static boolean DEBUG = System.getProperty("wcm") != null; + //*/ + + + private final Constant[] INITIALIZER_CONSTANTS = new Constant[] + { + new FieldrefConstant(InstructionSequenceMatcher.A, + InstructionSequenceMatcher.B, null, null), + }; + + // Instruction pattern: + // this.x = arg0; + // super.; + // return; + private final Instruction[] INITIALIZER_INSTRUCTIONS = new Instruction[] + { + new VariableInstruction(InstructionConstants.OP_ALOAD_0, 0), + new VariableInstruction(InstructionConstants.OP_ALOAD_1, 1), + new ConstantInstruction(InstructionConstants.OP_PUTFIELD, 0), + new VariableInstruction(InstructionConstants.OP_ALOAD_0, 0), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, InstructionSequenceMatcher.X), + new SimpleInstruction(InstructionConstants.OP_RETURN), + }; + + private final InstructionSequenceMatcher INITIALIZER_MATCHER = new InstructionSequenceMatcher(INITIALIZER_CONSTANTS, INITIALIZER_INSTRUCTIONS); + + // Fields acting as parameters and return values for the visitor methods. + private Clazz wrappedClass; + private int wrapCounter; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (programClass.subClasses == null || + programClass.subClasses.length == 0) + { + wrappedClass = null; + + // Can we find one non-static field with a class type? + wrapCounter = 0; + programClass.fieldsAccept(this); + if (wrapCounter == 1) + { + // Can we find exactly one initializer that initializes this + // field? + wrapCounter = 0; + programClass.methodsAccept(this); + if (wrapCounter == 1) + { + setWrappedClass(programClass, wrappedClass); + } + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Is the field non-static and of a class type? + if ((programField.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 && + ClassUtil.isInternalClassType(programField.getDescriptor(programClass)) && + !ClassUtil.isInternalArrayType(programField.getDescriptor(programClass))) + { + wrappedClass = programField.referencedClass; + if (wrappedClass != null) + { + wrapCounter++; + } + else + { + wrapCounter = Integer.MIN_VALUE; + } + } + else + { + wrapCounter = Integer.MIN_VALUE; + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Is the method an initializer? + if (ClassUtil.isInitializer(programMethod.getName(programClass))) + { + // Does it have exactly one parameter? + if (ClassUtil.internalMethodParameterCount(programMethod.getDescriptor(programClass)) == 1) + { + // Is the parameter a non-null reference? + Value value = + StoringInvocationUnit.getMethodParameterValue(programMethod, 1); + + if (value != null && + value.computationalType() == Value.TYPE_REFERENCE && + value.referenceValue().isNotNull() == Value.ALWAYS) + { + // Does the method initialize the field? + programMethod.attributesAccept(programClass, this); + } + else + { + wrapCounter = Integer.MIN_VALUE; + } + } + else + { + wrapCounter = Integer.MIN_VALUE; + } + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Is the initializer initializing the field? + if (codeAttribute.u4codeLength == 10) + { + INITIALIZER_MATCHER.reset(); + codeAttribute.instructionsAccept(clazz, method, INITIALIZER_MATCHER); + if (INITIALIZER_MATCHER.isMatching()) + { + String initializerClassName = clazz.getName(); + String fieldClassName = clazz.getClassName(INITIALIZER_MATCHER.matchedConstantIndex(InstructionSequenceMatcher.A)); + if (fieldClassName.equals(initializerClassName)) + { + wrapCounter++; + } + else + { + wrapCounter = Integer.MIN_VALUE; + } + } + else + { + wrapCounter = Integer.MIN_VALUE; + } + } + else + { + wrapCounter = Integer.MIN_VALUE; + } + } + + + // Small utility methods. + + private static void setWrappedClass(Clazz clazz, Clazz wrappedClass) + { + if (DEBUG) + { + System.out.println("WrapperClassMarker: ["+clazz.getName()+"] wraps ["+wrappedClass.getName()+"]"); + } + + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setWrappedClass(wrappedClass); + } + + + public static Clazz getWrappedClass(Clazz clazz) + { + return ClassOptimizationInfo.getClassOptimizationInfo(clazz).getWrappedClass(); + } +} diff --git a/src/proguard/optimize/info/package.html b/core/src/proguard/optimize/info/package.html similarity index 100% rename from src/proguard/optimize/info/package.html rename to core/src/proguard/optimize/info/package.html diff --git a/src/proguard/optimize/package.html b/core/src/proguard/optimize/package.html similarity index 100% rename from src/proguard/optimize/package.html rename to core/src/proguard/optimize/package.html diff --git a/src/proguard/evaluation/BranchTargetFinder.java b/core/src/proguard/optimize/peephole/BranchTargetFinder.java similarity index 81% rename from src/proguard/evaluation/BranchTargetFinder.java rename to core/src/proguard/optimize/peephole/BranchTargetFinder.java index 4b08773a5..195020d86 100644 --- a/src/proguard/evaluation/BranchTargetFinder.java +++ b/core/src/proguard/optimize/peephole/BranchTargetFinder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -18,7 +18,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package proguard.evaluation; +package proguard.optimize.peephole; import proguard.classfile.*; import proguard.classfile.attribute.*; @@ -28,15 +28,15 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; -import proguard.optimize.evaluation.PartialEvaluator; +import proguard.optimize.evaluation.InitializationFinder; import java.util.Arrays; /** - * This AttributeVisitor finds all instruction offsets, branch targets, - * exception offsets, and subroutine offsets in the CodeAttribute instances - * that it visits. + * This AttributeVisitor finds all instruction offsets, branch targets, and + * exception targets in the CodeAttribute objects that it visits. * + * @see InitializationFinder * @author Eric Lafortune */ public class BranchTargetFinder @@ -52,8 +52,6 @@ public class BranchTargetFinder private static boolean DEBUG = System.getProperty("btf") != null; //*/ - public static final int NONE = PartialEvaluator.NONE; - // We'll explicitly mark instructions that are not part of a subroutine, // with NO_SUBROUTINE. Subroutines may just branch back into normal code // (e.g. due to a break instruction in Java code), and we want to avoid @@ -63,31 +61,26 @@ public class BranchTargetFinder 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 static final short INSTRUCTION = 1 << 0; + private static final short CREATION = 1 << 1; + private static final short INITIALIZER = 1 << 2; + private static final short BRANCH_ORIGIN = 1 << 3; + private static final short BRANCH_TARGET = 1 << 4; + private static final short AFTER_BRANCH = 1 << 5; + private static final short EXCEPTION_START = 1 << 6; + private static final short EXCEPTION_END = 1 << 7; + private static final short EXCEPTION_HANDLER = 1 << 8; + private static final short SUBROUTINE_INVOCATION = 1 << 9; + private static final short SUBROUTINE_RETURNING = 1 << 10; 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; @@ -101,6 +94,28 @@ public boolean isInstruction(int offset) } + /** + * Returns whether the instruction at the given offset creates a new, + * uninitialized object instance, in the CodeAttribute that was visited + * most recently. + */ + public boolean isCreation(int offset) + { + return (instructionMarks[offset] & CREATION) != 0; + } + + + /** + * 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 (instructionMarks[offset] & INITIALIZER) != 0; + } + + /** * Returns whether the instruction at the given offset is the target of * any kind in the CodeAttribute that was visited most recently. @@ -168,7 +183,7 @@ public boolean isExceptionEnd(int offset) /** * Returns whether the instruction at the given offset is the start of an - * exception catch block in the CodeAttribute that was visited most recently. + * exception handler in the CodeAttribute that was visited most recently. */ public boolean isExceptionHandler(int offset) { @@ -236,70 +251,36 @@ public int subroutineEnd(int 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 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 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 whether the method is an instance initializer, in the +// * CodeAttribute that was visited most recently. +// */ +// public boolean isInitializer() +// { +// return superInitializationOffset != 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 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; +// } /** @@ -331,14 +312,12 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt instructionMarks = new short[codeLength + 1]; subroutineStarts = new int[codeLength]; subroutineEnds = new int[codeLength]; - creationOffsets = new int[codeLength]; - initializationOffsets = 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); +// Arrays.fill(initializationOffsets, 0, codeLength, NONE); } else { @@ -346,13 +325,12 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt 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); +// Arrays.fill(initializationOffsets, 0, codeLength, NONE); instructionMarks[codeLength] = 0; } - superInitializationOffset = NONE; +// superInitializationOffset = NONE; containsSubroutines = false; // Iterate until all subroutines have been fully marked. @@ -360,7 +338,6 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt { repeat = false; currentSubroutineStart = NO_SUBROUTINE; - recentCreationOffsetIndex = 0; // Mark branch targets by going over all instructions. codeAttribute.instructionsAccept(clazz, method, this); @@ -379,6 +356,7 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt // subroutine start. int previousSubroutineStart = NO_SUBROUTINE; + for (int offset = 0; offset < codeLength; offset++) { if (isInstruction(offset)) @@ -434,6 +412,7 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt if (isInstruction(index)) { System.out.println("" + + (isInitializer(index) ? 'I' : '-') + (isBranchOrigin(index) ? 'B' : '-') + (isAfterBranch(index) ? 'b' : '-') + (isBranchTarget(index) ? 'T' : '-') + @@ -444,7 +423,6 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt (isSubroutineStart(index) ? 'S' : '-') + (isSubroutineReturning(index) ? 'r' : '-') + (isSubroutine(index) ? " ["+subroutineStart(index)+" -> "+subroutineEnd(index)+"]" : "") + - (isNew(index) ? " ["+initializationOffset(index)+"] " : " ---- ") + InstructionFactory.create(codeAttribute.code, index).toString(index)); } } @@ -468,6 +446,7 @@ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute cod opcode == InstructionConstants.OP_FRETURN || opcode == InstructionConstants.OP_DRETURN || opcode == InstructionConstants.OP_ARETURN || + opcode == InstructionConstants.OP_RETURN || opcode == InstructionConstants.OP_ATHROW) { // Mark the branch origin. @@ -490,8 +469,8 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c byte opcode = constantInstruction.opcode; if (opcode == InstructionConstants.OP_NEW) { - // Push the 'new' instruction offset on the stack. - recentCreationOffsets[recentCreationOffsetIndex++] = offset; + // Mark the creation. + instructionMarks[offset] |= CREATION; } else if (opcode == InstructionConstants.OP_INVOKESPECIAL) { @@ -500,27 +479,8 @@ else if (opcode == InstructionConstants.OP_INVOKESPECIAL) clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); if (isInitializer) { - // Do we have any 'new' instruction offsets on the stack? - if (recentCreationOffsetIndex > 0) - { - // Pop the 'new' instruction offset from the stack. - int recentCreationOffset = recentCreationOffsets[--recentCreationOffsetIndex]; - - // Link the creation offset and the initialization offset. - // TODO: There could be multiple initialization offsets. - creationOffsets[offset] = recentCreationOffset; - - initializationOffsets[recentCreationOffset] = offset; - } - else - { - // Remember the super initialization offset. - // TODO: There could be multiple initialization offsets. - // For instance, in the constructor of the generated class - // groovy.inspect.swingui.GeneratedBytecodeAwareGroovyClassLoader - // in groovy-all-2.2.1.jar. - superInitializationOffset = offset; - } + // Mark the initializer. + instructionMarks[offset] |= INITIALIZER; } } } diff --git a/src/proguard/optimize/peephole/ClassFinalizer.java b/core/src/proguard/optimize/peephole/ClassFinalizer.java similarity index 97% rename from src/proguard/optimize/peephole/ClassFinalizer.java rename to core/src/proguard/optimize/peephole/ClassFinalizer.java index b0fe5a1db..b05233708 100644 --- a/src/proguard/optimize/peephole/ClassFinalizer.java +++ b/core/src/proguard/optimize/peephole/ClassFinalizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/ClassMerger.java b/core/src/proguard/optimize/peephole/ClassMerger.java similarity index 81% rename from src/proguard/optimize/peephole/ClassMerger.java rename to core/src/proguard/optimize/peephole/ClassMerger.java index 11ac0ca6a..9fcb8d2cf 100644 --- a/src/proguard/optimize/peephole/ClassMerger.java +++ b/core/src/proguard/optimize/peephole/ClassMerger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,7 +21,7 @@ package proguard.optimize.peephole; import proguard.classfile.*; -import proguard.classfile.attribute.visitor.AttributeNameFilter; +import proguard.classfile.attribute.visitor.*; import proguard.classfile.constant.visitor.*; import proguard.classfile.editor.*; import proguard.classfile.util.*; @@ -60,6 +60,7 @@ public class ClassMerger private final ProgramClass targetClass; private final boolean allowAccessModification; private final boolean mergeInterfacesAggressively; + private final boolean mergeWrapperClasses; private final ClassVisitor extraClassVisitor; private final MemberVisitor fieldOptimizationInfoCopier = new FieldOptimizationInfoCopier(); @@ -78,9 +79,14 @@ public class ClassMerger */ public ClassMerger(ProgramClass targetClass, boolean allowAccessModification, - boolean mergeInterfacesAggressively) + boolean mergeInterfacesAggressively, + boolean mergeWrapperClasses) { - this(targetClass, allowAccessModification, mergeInterfacesAggressively, null); + this(targetClass, + allowAccessModification, + mergeInterfacesAggressively, + mergeWrapperClasses, + null); } @@ -100,11 +106,13 @@ public ClassMerger(ProgramClass targetClass, public ClassMerger(ProgramClass targetClass, boolean allowAccessModification, boolean mergeInterfacesAggressively, + boolean mergeWrapperClasses, ClassVisitor extraClassVisitor) { this.targetClass = targetClass; this.allowAccessModification = allowAccessModification; this.mergeInterfacesAggressively = mergeInterfacesAggressively; + this.mergeWrapperClasses = mergeWrapperClasses; this.extraClassVisitor = extraClassVisitor; } @@ -152,9 +160,14 @@ public void visitProgramClass0(ProgramClass programClass) getTargetClass(programClass) == null && getTargetClass(targetClass) == null && - // Don't merge annotation classes, with all their introspection and + // Don't merge annotation classes, with all their reflection and // infinite recursion. - (programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) == 0 && + (programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATION) == 0 && + + (!DETAILS || print(programClass, "Version?")) && + + // Only merge classes with equal class versions. + programClass.u4version == targetClass.u4version && (!DETAILS || print(programClass, "Package visibility?")) && @@ -205,7 +218,7 @@ public void visitProgramClass0(ProgramClass programClass) // The two classes must have the same superclasses and interfaces // with static initializers. - initializedSuperClasses(programClass).equals(initializedSuperClasses(targetClass)) && + sideEffectSuperClasses(programClass).equals(sideEffectSuperClasses(targetClass)) && (!DETAILS || print(programClass, "Same instanceofed superclasses?")) && @@ -228,19 +241,22 @@ public void visitProgramClass0(ProgramClass programClass) (!DETAILS || print(programClass, "No clashing fields?")) && // The classes must not have clashing fields. - !haveAnyIdenticalFields(programClass, targetClass) && + (mergeWrapperClasses || + !haveAnyIdenticalFields(programClass, targetClass)) && (!DETAILS || print(programClass, "No unwanted fields?")) && // The two classes must not introduce any unwanted fields. - !introducesUnwantedFields(programClass, targetClass) && - !introducesUnwantedFields(targetClass, programClass) && + (mergeWrapperClasses || + !introducesUnwantedFields(programClass, targetClass) && + !introducesUnwantedFields(targetClass, programClass)) && (!DETAILS || print(programClass, "No shadowed fields?")) && // The two classes must not shadow each others fields. - !shadowsAnyFields(programClass, targetClass) && - !shadowsAnyFields(targetClass, programClass) && + (mergeWrapperClasses || + !shadowsAnyFields(programClass, targetClass) && + !shadowsAnyFields(targetClass, programClass)) && (!DETAILS || print(programClass, "No clashing methods?")) && @@ -265,7 +281,16 @@ public void visitProgramClass0(ProgramClass programClass) // The classes must not shadow each others non-private methods. !shadowsAnyMethods(programClass, targetClass) && - !shadowsAnyMethods(targetClass, programClass)) + !shadowsAnyMethods(targetClass, programClass) && + + (!DETAILS || print(programClass, "No non-copiable attributes?")) && + + // The class to be merged into the target class must not have + // non-copiable attributes (InnerClass, EnclosingMethod), + // unless it is a synthetic class. + (mergeWrapperClasses || + (programClass.getAccessFlags() & ClassConstants.ACC_SYNTHETIC) != 0 || + !hasNonCopiableAttributes(programClass))) { // We're not actually merging the classes, but only copying the // contents from the source class to the target class. We'll @@ -299,7 +324,7 @@ public void visitProgramClass0(ProgramClass programClass) sourceAccessFlags) & (ClassConstants.ACC_PUBLIC | ClassConstants.ACC_SUPER | - ClassConstants.ACC_ANNOTATTION | + ClassConstants.ACC_ANNOTATION | ClassConstants.ACC_ENUM)); // Copy over the superclass, if it's a non-interface class being @@ -328,8 +353,13 @@ public void visitProgramClass0(ProgramClass programClass) MemberAdder memberAdder = new MemberAdder(targetClass, fieldOptimizationInfoCopier); - programClass.fieldsAccept(memberAdder); - programClass.methodsAccept(memberAdder); + programClass.fieldsAccept(mergeWrapperClasses ? + new MemberAccessFilter(ClassConstants.ACC_STATIC, 0, memberAdder) : + memberAdder); + + programClass.methodsAccept(mergeWrapperClasses ? + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_INIT)), memberAdder) : + memberAdder); // Copy over the other attributes. programClass.attributesAccept( @@ -341,12 +371,8 @@ public void visitProgramClass0(ProgramClass programClass) 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)); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(targetClass) + .merge(ClassOptimizationInfo.getClassOptimizationInfo(programClass)); // Remember to replace the inlined class by the target class. setTargetClass(programClass, targetClass); @@ -437,14 +463,14 @@ private Set subInterfaces(Clazz clazz, Clazz exceptClass) /** * Returns the set of superclasses and interfaces that are initialized. */ - private Set initializedSuperClasses(Clazz clazz) + private Set sideEffectSuperClasses(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 SideEffectClassFilter( new ClassCollector(set))); return set; @@ -496,7 +522,8 @@ private Set caughtSuperClasses(Clazz clazz) * Returns whether the two given classes have fields with the same * names and descriptors. */ - private boolean haveAnyIdenticalFields(Clazz clazz, Clazz targetClass) + private boolean haveAnyIdenticalFields(Clazz clazz, + Clazz targetClass) { MemberCounter counter = new MemberCounter(); @@ -513,11 +540,11 @@ private boolean haveAnyIdenticalFields(Clazz clazz, Clazz targetClass) * Returns whether the given class would introduce any unwanted fields * in the target class. */ - private boolean introducesUnwantedFields(ProgramClass programClass, + private boolean introducesUnwantedFields(Clazz programClass, ProgramClass targetClass) { - // It's ok if the target class is never instantiated, without any other - // subclasses except for maybe the source class. + // It's ok if the target class is never instantiated and does not + // have any subclasses except for maybe the source class. if (!InstantiationClassMarker.isInstantiated(targetClass) && (targetClass.subClasses == null || isOnlySubClass(programClass, targetClass))) @@ -539,7 +566,8 @@ private boolean introducesUnwantedFields(ProgramClass programClass, * Returns whether the given class or its subclasses shadow any fields in * the given target class. */ - private boolean shadowsAnyFields(Clazz clazz, Clazz targetClass) + private boolean shadowsAnyFields(Clazz clazz, + Clazz targetClass) { MemberCounter counter = new MemberCounter(); @@ -559,7 +587,8 @@ private boolean shadowsAnyFields(Clazz clazz, Clazz targetClass) * Returns whether the two given classes have class members with the same * name and descriptor. */ - private boolean haveAnyIdenticalMethods(Clazz clazz, Clazz targetClass) + private boolean haveAnyIdenticalMethods(Clazz clazz, + Clazz targetClass) { MemberCounter counter = new MemberCounter(); @@ -581,8 +610,8 @@ private boolean haveAnyIdenticalMethods(Clazz clazz, Clazz targetClass) 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. + // It's ok if the target class is already abstract and does not + // have any subclasses except for maybe the source class. if ((targetClass.getAccessFlags() & (ClassConstants.ACC_ABSTRACT | ClassConstants.ACC_INTERFACE)) != 0 && @@ -598,13 +627,13 @@ private boolean introducesUnwantedAbstractMethods(Clazz clazz, // Collect all abstract methods, and similar abstract methods in the // class hierarchy of the target class. clazz.methodsAccept(new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, - new MultiMemberVisitor(new MemberVisitor[] - { + new MultiMemberVisitor( counter, + new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, - new MemberCollector(targetSet))) - }))); + new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, + new MemberCollector(false, true, true, targetSet))) + ))); return targetSet.size() < counter.getCount(); } @@ -614,13 +643,23 @@ private boolean introducesUnwantedAbstractMethods(Clazz clazz, * Returns whether the given class overrides any methods in the given * target class. */ - private boolean overridesAnyMethods(Clazz clazz, Clazz targetClass) + private boolean overridesAnyMethods(Clazz clazz, + ProgramClass targetClass) { + // It's ok if the target class is never instantiated and does + // not have any subclasses except for maybe the source class. + if (!InstantiationClassMarker.isInstantiated(targetClass) && + (targetClass.subClasses == null || + isOnlySubClass(clazz, targetClass))) + { + return false; + } + 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.ACC_PRIVATE | ClassConstants.ACC_STATIC | ClassConstants.ACC_ABSTRACT, + // Visit all non-abstract methods, counting the ones that are + // overriding methods in the class hierarchy of the target class. + clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.ACC_ABSTRACT, new InitializerMethodFilter(null, new SimilarMemberVisitor(targetClass, true, true, false, false, new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ClassConstants.ACC_ABSTRACT, @@ -631,11 +670,20 @@ private boolean overridesAnyMethods(Clazz clazz, Clazz targetClass) /** - * Returns whether the given class or its subclasses shadow any methods in - * the given target class. + * Returns whether the given class or its subclasses have private or + * static methods that shadow any methods in the given target class. */ - private boolean shadowsAnyMethods(Clazz clazz, Clazz targetClass) + private boolean shadowsAnyMethods(Clazz clazz, + Clazz targetClass) { + // It's ok if the source class already extends the target class + // or (in practice) vice versa. + if (clazz.extends_(targetClass) || + targetClass.extends_(clazz)) + { + return false; + } + MemberCounter counter = new MemberCounter(); // Visit all methods, counting the ones that are shadowing @@ -645,8 +693,7 @@ private boolean shadowsAnyMethods(Clazz clazz, Clazz targetClass) new InitializerMethodFilter(null, new SimilarMemberVisitor(targetClass, true, true, false, false, new MemberAccessFilter(ClassConstants.ACC_FINAL, 0, - counter))))); - + counter))))); if (counter.getCount() > 0) { return true; @@ -657,11 +704,10 @@ private boolean shadowsAnyMethods(Clazz clazz, Clazz targetClass) clazz.hierarchyAccept(true, false, false, true, new AllMethodVisitor( new MemberAccessFilter(ClassConstants.ACC_PRIVATE, 0, - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_INIT)), + new InitializerMethodFilter(null, new SimilarMemberVisitor(targetClass, true, true, true, false, new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, - counter)))))); - + counter)))))); if (counter.getCount() > 0) { return true; @@ -672,7 +718,7 @@ private boolean shadowsAnyMethods(Clazz clazz, Clazz targetClass) clazz.hierarchyAccept(true, false, false, true, new AllMethodVisitor( new MemberAccessFilter(ClassConstants.ACC_STATIC, 0, - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_CLINIT)), + new InitializerMethodFilter(null, new SimilarMemberVisitor(targetClass, true, true, true, false, new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, counter)))))); @@ -681,13 +727,28 @@ private boolean shadowsAnyMethods(Clazz clazz, Clazz targetClass) } + /** + * Returns whether the given class has any attributes that can not be copied when + * merging it into another class. + */ + private boolean hasNonCopiableAttributes(Clazz clazz) + { + AttributeCounter counter = new AttributeCounter(); + + // Copy over the other attributes. + clazz.attributesAccept( + new AttributeNameFilter( + new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_InnerClasses), + new FixedStringMatcher(ClassConstants.ATTR_EnclosingMethod)), + counter)); + + return counter.getCount() > 0; + } + + public static void setTargetClass(Clazz clazz, Clazz targetClass) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info != null) - { - info.setTargetClass(targetClass); - } + ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(clazz).setTargetClass(targetClass); } @@ -698,13 +759,7 @@ public static Clazz getTargetClass(Clazz clazz) // Return the last target class, if any. while (true) { - ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); - if (info == null) - { - return targetClass; - } - - clazz = info.getTargetClass(); + clazz = ClassOptimizationInfo.getClassOptimizationInfo(clazz).getTargetClass(); if (clazz == null) { return targetClass; @@ -728,8 +783,8 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie ProgramField copiedField = (ProgramField)programField.getVisitorInfo(); Object info = copiedField.getVisitorInfo(); - programField.setVisitorInfo(info instanceof FieldOptimizationInfo ? - new FieldOptimizationInfo((FieldOptimizationInfo)info) : + programField.setVisitorInfo(info instanceof ProgramFieldOptimizationInfo ? + new ProgramFieldOptimizationInfo((ProgramFieldOptimizationInfo)info) : info); } diff --git a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java b/core/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java similarity index 98% rename from src/proguard/optimize/peephole/GotoCommonCodeReplacer.java rename to core/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java index c1bf6bf23..6d7db5a7b 100644 --- a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java +++ b/core/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,7 +27,6 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; -import proguard.evaluation.BranchTargetFinder; /** * This AttributeVisitor redirects unconditional branches so any common code @@ -51,7 +50,7 @@ public class GotoCommonCodeReplacer private final InstructionVisitor extraInstructionVisitor; private final BranchTargetFinder branchTargetFinder = new BranchTargetFinder(); - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, false); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); /** @@ -134,7 +133,7 @@ public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute cod if (newBranchOffset != branchInstruction.length(offset)) { Instruction newGotoInstruction = - new BranchInstruction(opcode, newBranchOffset).shrink(); + new BranchInstruction(opcode, newBranchOffset); codeAttributeEditor.replaceInstruction(offset, newGotoInstruction); } diff --git a/src/proguard/optimize/peephole/GotoGotoReplacer.java b/core/src/proguard/optimize/peephole/GotoGotoReplacer.java similarity index 98% rename from src/proguard/optimize/peephole/GotoGotoReplacer.java rename to core/src/proguard/optimize/peephole/GotoGotoReplacer.java index de31a69dc..c9d8e0d81 100644 --- a/src/proguard/optimize/peephole/GotoGotoReplacer.java +++ b/core/src/proguard/optimize/peephole/GotoGotoReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/core/src/proguard/optimize/peephole/GotoReturnReplacer.java similarity index 98% rename from src/proguard/optimize/peephole/GotoReturnReplacer.java rename to core/src/proguard/optimize/peephole/GotoReturnReplacer.java index 9cf8b64ee..2f2efd2f1 100644 --- a/src/proguard/optimize/peephole/GotoReturnReplacer.java +++ b/core/src/proguard/optimize/peephole/GotoReturnReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/HorizontalClassMerger.java b/core/src/proguard/optimize/peephole/HorizontalClassMerger.java similarity index 89% rename from src/proguard/optimize/peephole/HorizontalClassMerger.java rename to core/src/proguard/optimize/peephole/HorizontalClassMerger.java index 8f5757485..3809946f6 100644 --- a/src/proguard/optimize/peephole/HorizontalClassMerger.java +++ b/core/src/proguard/optimize/peephole/HorizontalClassMerger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -35,13 +35,14 @@ public class HorizontalClassMerger extends SimplifiedVisitor implements ClassVisitor { - private final boolean allowAccessModification; - private final boolean mergeInterfacesAggressively; - private final ClassVisitor extraClassVisitor; + 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. @@ -57,6 +58,7 @@ public HorizontalClassMerger(boolean allowAccessModification, /** * Creates a new VerticalClassMerger. + * * @param allowAccessModification specifies whether the access modifiers * of classes can be changed in order to * merge them. @@ -67,7 +69,7 @@ public HorizontalClassMerger(boolean allowAccessModification, */ public HorizontalClassMerger(boolean allowAccessModification, boolean mergeInterfacesAggressively, - ClassVisitor extraClassVisitor) + ClassVisitor extraClassVisitor ) { this.allowAccessModification = allowAccessModification; this.mergeInterfacesAggressively = mergeInterfacesAggressively; @@ -84,6 +86,7 @@ public void visitProgramClass(ProgramClass programClass) new ClassMerger(programClass, allowAccessModification, mergeInterfacesAggressively, + false, extraClassVisitor)))); } } \ No newline at end of file diff --git a/core/src/proguard/optimize/peephole/InstructionSequenceConstants.java b/core/src/proguard/optimize/peephole/InstructionSequenceConstants.java new file mode 100644 index 000000000..574daf759 --- /dev/null +++ b/core/src/proguard/optimize/peephole/InstructionSequenceConstants.java @@ -0,0 +1,4772 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.editor.*; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.visitor.ClassPrinter; + +/** + * This class contains a set of instruction sequences with their suggested + * more compact or more efficient replacements. + * + * @see InstructionSequencesReplacer + * @see InstructionSequenceReplacer + * @author Eric Lafortune + */ +public class InstructionSequenceConstants +{ + // The arrays with constants and instructions used to be static, + // but now they are initialized with references to classes and + // class members, inside an instance of this class. As an added + // benefit, they can be garbage collected after they have been used. + public final Instruction[][][] VARIABLE_SEQUENCES; + public final Instruction[][][] ARITHMETIC_SEQUENCES; + public final Instruction[][][] FIELD_SEQUENCES; + public final Instruction[][][] CAST_SEQUENCES; + public final Instruction[][][] BRANCH_SEQUENCES; + public final Instruction[][][] STRING_SEQUENCES; + public final Instruction[][][] OBJECT_SEQUENCES; + public final Instruction[][][] MATH_SEQUENCES; + public final Instruction[][][] MATH_ANDROID_SEQUENCES; + + public final Constant[] CONSTANTS; + + // Internal short-hand constants. + private static final String BOOLEAN = ClassConstants.NAME_JAVA_LANG_BOOLEAN; + private static final String BYTE = ClassConstants.NAME_JAVA_LANG_BYTE; + private static final String CHARACTER = ClassConstants.NAME_JAVA_LANG_CHARACTER; + private static final String SHORT = ClassConstants.NAME_JAVA_LANG_SHORT; + private static final String INTEGER = ClassConstants.NAME_JAVA_LANG_INTEGER; + private static final String LONG = ClassConstants.NAME_JAVA_LANG_LONG; + private static final String FLOAT = ClassConstants.NAME_JAVA_LANG_FLOAT; + private static final String DOUBLE = ClassConstants.NAME_JAVA_LANG_DOUBLE; + private static final String STRING = ClassConstants.NAME_JAVA_LANG_STRING; + private static final String STRING_BUFFER = ClassConstants.NAME_JAVA_LANG_STRING_BUFFER; + private static final String STRING_BUILDER = ClassConstants.NAME_JAVA_LANG_STRING_BUILDER; + private static final String MATH = ClassConstants.NAME_JAVA_LANG_MATH; + private static final String FLOAT_MATH = ClassConstants.NAME_ANDROID_UTIL_FLOAT_MATH; + + 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; + + // Replacement constants that are derived from matched variables. + 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; + + + /** + * Creates a new instance of InstructionSequenceConstants, with constants + * that reference classes from the given class pools. + */ + public InstructionSequenceConstants(ClassPool programClassPool, + ClassPool libraryClassPool) + { + InstructionSequenceBuilder ____ = + new InstructionSequenceBuilder(programClassPool, + libraryClassPool); + + // Create fieldref constants with wildcards, for fields in class X, + // with name Y, and the given primitive types. + ConstantPoolEditor constantPoolEditor = ____.getConstantPoolEditor(); + final int FIELD_Z = constantPoolEditor.addConstant(new FieldrefConstant(X, constantPoolEditor.addConstant(new NameAndTypeConstant(Y, constantPoolEditor.addUtf8Constant("Z"))), null, null)); + final int FIELD_B = constantPoolEditor.addConstant(new FieldrefConstant(X, constantPoolEditor.addConstant(new NameAndTypeConstant(Y, constantPoolEditor.addUtf8Constant("B"))), null, null)); + final int FIELD_C = constantPoolEditor.addConstant(new FieldrefConstant(X, constantPoolEditor.addConstant(new NameAndTypeConstant(Y, constantPoolEditor.addUtf8Constant("C"))), null, null)); + final int FIELD_S = constantPoolEditor.addConstant(new FieldrefConstant(X, constantPoolEditor.addConstant(new NameAndTypeConstant(Y, constantPoolEditor.addUtf8Constant("S"))), null, null)); + final int FIELD_I = constantPoolEditor.addConstant(new FieldrefConstant(X, constantPoolEditor.addConstant(new NameAndTypeConstant(Y, constantPoolEditor.addUtf8Constant("I"))), null, null)); + final int FIELD_F = constantPoolEditor.addConstant(new FieldrefConstant(X, constantPoolEditor.addConstant(new NameAndTypeConstant(Y, constantPoolEditor.addUtf8Constant("F"))), null, null)); + final int FIELD_J = constantPoolEditor.addConstant(new FieldrefConstant(X, constantPoolEditor.addConstant(new NameAndTypeConstant(Y, constantPoolEditor.addUtf8Constant("J"))), null, null)); + final int FIELD_D = constantPoolEditor.addConstant(new FieldrefConstant(X, constantPoolEditor.addConstant(new NameAndTypeConstant(Y, constantPoolEditor.addUtf8Constant("D"))), null, null)); + + // Create methodref constants with wildcards, for methods in class X, + // with the given names and descriptors. + final int EQUALS = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant(ClassConstants.METHOD_NAME_EQUALS, ClassConstants.METHOD_TYPE_EQUALS), null, null)); + final int TO_STRING = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant(ClassConstants.METHOD_NAME_TOSTRING, ClassConstants.METHOD_TYPE_TOSTRING), null, null)); + final int BOOLEAN_VALUE = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant("booleanValue", "()Z"), null, null)); + final int BYTE_VALUE = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant("byteValue", "()B"), null, null)); + final int CHAR_VALUE = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant("charValue", "()C"), null, null)); + final int SHORT_VALUE = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant("shortValue", "()S"), null, null)); + final int INT_VALUE = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant("intValue", "()I"), null, null)); + final int FLOAT_VALUE = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant("floatValue", "()F"), null, null)); + final int LONG_VALUE = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant("longValue", "()J"), null, null)); + final int DOUBLE_VALUE = constantPoolEditor.addConstant(new MethodrefConstant(X, constantPoolEditor.addNameAndTypeConstant("doubleValue", "()D"), null, null)); + + final InstructionSequenceReplacer.Label TRY_START = InstructionSequenceReplacer.label(); + final InstructionSequenceReplacer.Label TRY_END = InstructionSequenceReplacer.label(); + final InstructionSequenceReplacer.Label CATCH_END = InstructionSequenceReplacer.label(); + + final InstructionSequenceReplacer.Label CATCH_EXCEPTION = InstructionSequenceReplacer.catch_(TRY_START.offset(), TRY_END.offset(), constantPoolEditor.addClassConstant(ClassConstants.NAME_JAVA_LANG_EXCEPTION, null)); + + VARIABLE_SEQUENCES = new Instruction[][][] + { + { // nop = nothing + ____.nop().__(), + }, + { // iload/pop = nothing + ____.iload(X) + .pop().__(), + }, + { // lload/pop2 = nothing + ____.lload(X) + .pop2().__(), + }, + { // fload/pop = nothing + ____.fload(X) + .pop().__(), + }, + { // dload/pop2 = nothing + ____.dload(X) + .pop2().__(), + }, + { // aload/pop = nothing + ____.aload(X) + .pop().__(), + }, + { // i = i = nothing + ____.iload(X) + .istore(X).__(), + }, + { // l = l = nothing + ____.lload(X) + .lstore(X).__(), + }, + { // f = f = nothing + ____.fload(X) + .fstore(X).__(), + }, + { // d = d = nothing + ____.dload(X) + .dstore(X).__(), + }, + { // a = a = nothing + ____.aload(X) + .astore(X).__(), + }, + { // iload/iload = iload/dup + ____.iload(X) + .iload(X).__(), + + ____.iload(X) + .dup().__() + }, + { // lload/lload = lload/dup2 + ____.lload(X) + .lload(X).__(), + + ____.lload(X) + .dup2().__() + }, + { // fload/fload = fload/dup + ____.fload(X) + .fload(X).__(), + + ____.fload(X) + .dup().__() + }, + { // dload/dload = dload/dup2 + ____.dload(X) + .dload(X).__(), + + ____.dload(X) + .dup2().__() + }, + { // aload/aload = aload/dup + ____.aload(X) + .aload(X).__(), + + ____.aload(X) + .dup().__() + }, + { // istore/istore = pop/istore + ____.istore(X) + .istore(X).__(), + + ____.pop() + .istore(X).__() + }, + { // lstore/lstore = pop2/lstore + ____.lstore(X) + .lstore(X).__(), + + ____.pop2() + .lstore(X).__() + }, + { // fstore/fstore = pop/fstore + ____.fstore(X) + .fstore(X).__(), + + ____.pop() + .fstore(X).__() + }, + { // dstore/dstore = pop2/dstore + ____.dstore(X) + .dstore(X).__(), + + ____.pop2() + .dstore(X).__() + }, + { // astore/astore = pop/astore + ____.astore(X) + .astore(X).__(), + + ____.pop() + .astore(X).__() + }, + { // istore/iload = dup/istore + ____.istore(X) + .iload(X).__(), + + ____.dup() + .istore(X).__() + }, + { // lstore/lload = dup2/lstore + ____.lstore(X) + .lload(X).__(), + + ____.dup2() + .lstore(X).__() + }, + { // fstore/fload = dup/fstore + ____.fstore(X) + .fload(X).__(), + + ____.dup() + .fstore(X).__() + }, + { // dstore/dload = dup2/dstore + ____.dstore(X) + .dload(X).__(), + + ____.dup2() + .dstore(X).__() + }, + { // astore/aload = dup/astore + ____.astore(X) + .aload(X).__(), + + ____.dup() + .astore(X).__() + }, + { // iload/dup/istore = iload + ____.iload(X) + .dup() + .istore(X).__(), + + ____.iload(X).__() + }, + { // lload/dup2/lstore = lload + ____.lload(X) + .dup2() + .lstore(X).__(), + + ____.lload(X).__() + }, + { // fload/dup/fstore = iload + ____.fload(X) + .dup() + .fstore(X).__(), + + ____.fload(X).__() + }, + { // dload/dup2/dstore = dload + ____.dload(X) + .dup2() + .dstore(X).__(), + + ____.dload(X).__() + }, + { // aload/dup/astore = aload + ____.aload(X) + .dup() + .astore(X).__(), + + ____.aload(X).__() + }, + }; + + ARITHMETIC_SEQUENCES = new Instruction[][][] + { + { // c + i = i + c + ____.iconst(A) + .iload(X) + .iadd().__(), + + ____.iload(X) + .iconst(A) + .iadd().__() + }, + { // b + i = i + b + ____.bipush(A) + .iload(X) + .iadd().__(), + + ____.iload(X) + .bipush(A) + .iadd().__() + }, + { // s + i = i + s + ____.sipush(A) + .iload(X) + .iadd().__(), + + ____.iload(X) + .sipush(A) + .iadd().__() + }, + { // c + i = i + c + ____.ldc_(A) + .iload(X) + .iadd().__(), + + ____.iload(X) + .ldc_(A) + .iadd().__() + }, + { // c * i = i * c + ____.sipush(A) + .iload(X) + .imul().__(), + + ____.iload(X) + .sipush(A) + .imul().__() + }, + { // b * i = i * b + ____.bipush(A) + .iload(X) + .imul().__(), + + ____.iload(X) + .bipush(A) + .imul().__() + }, + { // s * i = i * s + ____.sipush(A) + .iload(X) + .imul().__(), + + ____.iload(X) + .sipush(A) + .imul().__() + }, + { // c * i = i * c + ____.ldc_(A) + .iload(X) + .imul().__(), + + ____.iload(X) + .ldc_(A) + .imul().__() + }, + { // c + l = l + c + ____.lconst(A) + .lload(X) + .ladd().__(), + + ____.lload(X) + .lconst(A) + .ladd().__() + }, + { // c + l = l + c + ____.ldc2_w(A) + .lload(X) + .ladd().__(), + + ____.lload(X) + .ldc2_w(A) + .ladd().__() + }, + { // c * l = l * c + ____.lconst(A) + .lload(X) + .lmul().__(), + + ____.lload(X) + .lconst(A) + .lmul().__() + }, + { // c + f = f + c + ____.fconst(A) + .fload(X) + .fadd().__(), + + ____.fload(X) + .fconst(A) + .fadd().__() + }, + { // c + f = f + c + ____.ldc_(A) + .fload(X) + .fadd().__(), + + ____.fload(X) + .ldc_(A) + .fadd().__() + }, + { // c * f = f * c + ____.fconst(A) + .fload(X) + .fmul().__(), + + ____.fload(X) + .fconst(A) + .fmul().__() + }, + { // c * f = f * c + ____.ldc_(A) + .fload(X) + .lmul().__(), + + ____.fload(X) + .ldc_(A) + .lmul().__() + }, + { // c + d = d + c + ____.dconst(A) + .dload(X) + .dadd().__(), + + ____.dload(X) + .dconst(A) + .dadd().__() + }, + { // c + d = d + c + ____.ldc2_w(A) + .dload(X) + .dadd().__(), + + ____.dload(X) + .ldc2_w(A) + .dadd().__() + }, + { // c * d = d * c + ____.dconst(A) + .dload(X) + .dmul().__(), + + ____.dload(X) + .dconst(A) + .dmul().__() + }, + { // c * d = d * c + ____.ldc2_w(A) + .dload(X) + .dmul().__(), + + ____.dload(X) + .ldc2_w(A) + .dmul().__() + }, + { // i = i + c = i += c + ____.iload(X) + .sipush(A) + .iadd() + .istore(X).__(), + + ____.iinc(X, A).__() + }, + { // i = i + b = i += b + ____.iload(X) + .bipush(A) + .iadd() + .istore(X).__(), + + ____.iinc(X, A).__() + }, + { // i = i + s = i += s + ____.iload(X) + .sipush(A) + .iadd() + .istore(X).__(), + + ____.iinc(X, A).__() + }, + { // i = i - -1 = i++ + ____.iload(X) + .iconst_m1() + .isub() + .istore(X).__(), + + ____.iinc(X, 1).__() + }, + { // i = i - 1 = i-- + ____.iload(X) + .iconst_1() + .isub() + .istore(X).__(), + + ____.iinc(X, -1).__() + }, + { // i = i - 2 = i -= 2 + ____.iload(X) + .iconst_2() + .isub() + .istore(X).__(), + + ____.iinc(X, -2).__() + }, + { // i = i - 3 = i -= 3 + ____.iload(X) + .iconst_3() + .isub() + .istore(X).__(), + + ____.iinc(X, -3).__() + }, + { // i = i - 4 = i -= 4 + ____.iload(X) + .iconst_4() + .isub() + .istore(X).__(), + + ____.iinc(X, -4).__() + }, + { // i = i - 5 = i -= 5 + ____.iload(X) + .iconst_5() + .isub() + .istore(X).__(), + + ____.iinc(X, -5).__() + }, + { // ... + 0 = ... + ____.iconst_0() + .iadd().__(), + }, + { // ... + 0L = ... + ____.lconst_0() + .ladd().__(), + }, + // Not valid for -0.0. +// { // ... + 0f = ... +// ____.fconst_0() +// .fadd().__(), +// +// }, +// { // ... + 0d = ... +// ____.dconst_0() +// .dadd().__(), +// +// }, + { // ... - 0 = ... + ____.iconst_0() + .isub().__(), + }, + { // ... - 0L = ... + ____.lconst_0() + .lsub().__(), + }, + { // ... - 0f = ... + ____.fconst_0() + .fsub().__(), + }, + { // ... - 0d = ... + ____.dconst_0() + .dsub().__(), + }, + { // ... * -1 = -... + ____.iconst_m1() + .imul().__(), + + ____.ineg().__() + }, + { // ... * 0 = 0 + ____.iconst_0() + .imul().__(), + + ____.pop() + .iconst_0().__() + }, + { // ... * 1 = ... + ____.iconst_1() + .imul().__(), + }, + { // ... * 2 = ... << 1 + ____.iconst_2() + .imul().__(), + + ____.iconst_1() + .ishl().__() + }, + { // ... * 4 = ... << 2 + ____.iconst_4() + .imul().__(), + + ____.iconst_2() + .ishl().__() + }, + { // ... * 8 = ... << 3 + ____.bipush(8) + .imul().__(), + + ____.iconst_3() + .ishl().__() + }, + { // ... * 16 = ... << 4 + ____.bipush(16) + .imul().__(), + + ____.bipush(4) + .ishl().__() + }, + { // ... * 32 = ... << 5 + ____.bipush(32) + .imul().__(), + + ____.bipush(5) + .ishl().__() + }, + { // ... * 64 = ... << 6 + ____.bipush(64) + .imul().__(), + + ____.bipush(6) + .ishl().__() + }, + { // ... * 128 = ... << 7 + ____.sipush(128) + .imul().__(), + + ____.bipush(7) + .ishl().__() + }, + { // ... * 256 = ... << 8 + ____.sipush(256) + .imul().__(), + + ____.bipush(8) + .ishl().__() + }, + { // ... * 512 = ... << 9 + ____.sipush(512) + .imul().__(), + + ____.bipush(9) + .ishl().__() + }, + { // ... * 1024 = ... << 10 + ____.sipush(1024) + .imul().__(), + + ____.bipush(10) + .ishl().__() + }, + { // ... * 2048 = ... << 11 + ____.sipush(2048) + .imul().__(), + + ____.bipush(11) + .ishl().__() + }, + { // ... * 4096 = ... << 12 + ____.sipush(4096) + .imul().__(), + + ____.bipush(12) + .ishl().__() + }, + { // ... * 8192 = ... << 13 + ____.sipush(8192) + .imul().__(), + + ____.bipush(13) + .ishl().__() + }, + { // ... * 16384 = ... << 14 + ____.sipush(16384) + .imul().__(), + + ____.bipush(14) + .ishl().__() + }, + { // ... * 32768 = ... << 15 + ____.ldc(32768) + .imul().__(), + + ____.bipush(15) + .ishl().__() + }, + { // ... * 65536 = ... << 16 + ____.ldc(65536) + .imul().__(), + + ____.bipush(16) + .ishl().__() + }, + { // ... * 16777216 = ... << 24 + ____.ldc(16777216) + .imul().__(), + + ____.bipush(24) + .ishl().__() + }, + { // ... * -1L = -... + ____.ldc2_w(-1L) + .lmul().__(), + + ____.lneg().__() + }, + { // ... * 0L = 0L + ____.lconst_0() + .lmul().__(), + + ____.pop2() + .lconst_0().__() + }, + { // ... * 1L = ... + ____.lconst_1() + .lmul().__(), + }, + { // ... * 2L = ... << 1 + ____.ldc2_w(2L) + .lmul().__(), + + ____.iconst_1() + .lshl().__() + }, + { // ... * 4L = ... << 2 + ____.ldc2_w(4L) + .lmul().__(), + + ____.iconst_2() + .lshl().__() + }, + { // ... * 8L = ... << 3 + ____.ldc2_w(8L) + .lmul().__(), + + ____.iconst_3() + .lshl().__() + }, + { // ... * 16L = ... << 4 + ____.ldc2_w(16L) + .lmul().__(), + + ____.bipush(4) + .lshl().__() + }, + { // ... * 32L = ... << 5 + ____.ldc2_w(32L) + .lmul().__(), + + ____.bipush(5) + .lshl().__() + }, + { // ... * 64L = ... << 6 + ____.ldc2_w(64L) + .lmul().__(), + + ____.bipush(6) + .lshl().__() + }, + { // ... * 128L = ... << 7 + ____.ldc2_w(128L) + .lmul().__(), + + ____.bipush(7) + .lshl().__() + }, + { // ... * 256L = ... << 8 + ____.ldc2_w(256L) + .lmul().__(), + + ____.bipush(8) + .lshl().__() + }, + { // ... * 512L = ... << 9 + ____.ldc2_w(512L) + .lmul().__(), + + ____.bipush(9) + .lshl().__() + }, + { // ... * 1024L = ... << 10 + ____.ldc2_w(1024L) + .lmul().__(), + + ____.bipush(10) + .lshl().__() + }, + { // ... * 2048L = ... << 11 + ____.ldc2_w(2048L) + .lmul().__(), + + ____.bipush(11) + .lshl().__() + }, + { // ... * 4096L = ... << 12 + ____.ldc2_w(4096L) + .lmul().__(), + + ____.bipush(12) + .lshl().__() + }, + { // ... * 8192L = ... << 13 + ____.ldc2_w(8192L) + .lmul().__(), + + ____.bipush(13) + .lshl().__() + }, + { // ... * 16384L = ... << 14 + ____.ldc2_w(16384L) + .lmul().__(), + + ____.bipush(14) + .lshl().__() + }, + { // ... * 32768L = ... << 15 + ____.ldc2_w(32768L) + .lmul().__(), + + ____.bipush(15) + .lshl().__() + }, + { // ... * 65536LL = ... << 16 + ____.ldc2_w(65536L) + .lmul().__(), + + ____.bipush(16) + .lshl().__() + }, + { // ... * 16777216L = ... << 24 + ____.ldc2_w(16777216L) + .lmul().__(), + + ____.bipush(24) + .lshl().__() + }, + { // ... * 4294967296L = ... << 32 + ____.ldc2_w(4294967296L) + .lmul().__(), + + ____.bipush(32) + .lshl().__() + }, + { // ... * -1f = -... + ____.ldc(-1f) + .fmul().__(), + + ____.fneg().__() + }, + // Not valid for -0.0 and for NaN. +// { // ... * 0f = 0f +// ____.fconst_0() +// .fmul().__(), +// +// ____.pop() +// .fconst_0().__() +// }, + { // ... * 1f = ... + ____.fconst_1() + .fmul().__(), + }, + { // ... * -1d = -... + ____.ldc2_w(-1.) + .dmul().__(), + + ____.dneg().__() + }, + // Not valid for -0.0 and for NaN. +// { // ... * 0d = 0d +// ____.dconst_0() +// .dmul().__(), +// +// ____.pop2() +// .dconst_0().__() +// }, + { // ... * 1d = ... + ____.dconst_1() + .dmul().__(), + }, + { // ... / -1 = -... + ____.iconst_m1() + .idiv().__(), + + ____.ineg().__() + }, + { // ... / 1 = ... + ____.iconst_1() + .idiv().__(), + }, + // Not valid for negative values. +// { // ... / 2 = ... >> 1 +// ____.iconst_2() +// .idiv().__(), +// +// ____.iconst_1() +// .ishr().__() +// }, +// { // ... / 4 = ... >> 2 +// ____.iconst_4() +// .idiv().__(), +// +// ____.iconst_2() +// .ishr().__() +// }, +// { // ... / 8 = ... >> 3 +// ____.bipush(8) +// .idiv().__(), +// +// ____.iconst_3() +// .ishr().__() +// }, +// { // ... / 16 = ... >> 4 +// ____.bipush(16) +// .idiv().__(), +// +// ____.bipush(4) +// .ishr().__() +// }, +// { // ... / 32 = ... >> 5 +// ____.bipush(32) +// .idiv().__(), +// +// ____.bipush(5) +// .ishr().__() +// }, +// { // ... / 64 = ... >> 6 +// ____.bipush(64) +// .idiv().__(), +// +// ____.bipush(6) +// .ishr().__() +// }, +// { // ... / 128 = ... >> 7 +// ____.sipush(128) +// .idiv().__(), +// +// ____.bipush(7) +// .ishr().__() +// }, +// { // ... / 256 = ... >> 8 +// ____.sipush(256) +// .idiv().__(), +// +// ____.bipush(8) +// .ishr().__() +// }, +// { // ... / 512 = ... >> 9 +// ____.sipush(512) +// .idiv().__(), +// +// ____.bipush(9) +// .ishr().__() +// }, +// { // ... / 1024 = ... >> 10 +// ____.sipush(1024) +// .idiv().__(), +// +// ____.bipush(10) +// .ishr().__() +// }, +// { // ... / 2048 = ... >> 11 +// ____.sipush(2048) +// .idiv().__(), +// +// ____.bipush(11) +// .ishr().__() +// }, +// { // ... / 4096 = ... >> 12 +// ____.sipush(4096) +// .idiv().__(), +// +// ____.bipush(12) +// .ishr().__() +// }, +// { // ... / 8192 = ... >> 13 +// ____.sipush(8192) +// .idiv().__(), +// +// ____.bipush(13) +// .ishr().__() +// }, +// { // ... / 16384 = ... >> 14 +// ____.sipush(16384) +// .idiv().__(), +// +// ____.bipush(14) +// .ishr().__() +// }, +// { // ... / 32768 = ... >> 15 +// ____.ldc(32768) +// .idiv().__(), +// +// ____.bipush(15) +// .ishr().__() +// }, +// { // ... / 65536 = ... >> 16 +// ____.ldc(65536) +// .idiv().__(), +// +// ____.bipush(16) +// .ishr().__() +// }, +// { // ... / 16777216 = ... >> 24 +// ____.ldc(16777216) +// .idiv().__(), +// +// ____.bipush(24) +// .ishr().__() +// }, + { // ... / -1L = -... + ____.ldc2_w(-1L) + .ldiv().__(), + + ____.lneg().__() + }, + { // ... / 1L = ... + ____.lconst_1() + .ldiv().__(), + }, + // Not valid for negative values. +// { // ... / 2L = ... >> 1 +// ____.ldc2_w(2L) +// .ldiv().__(), +// +// ____.iconst_1() +// .lshr().__() +// }, +// { // ... / 4L = ... >> 2 +// ____.ldc2_w(4L) +// .ldiv().__(), +// +// ____.iconst_2() +// .lshr().__() +// }, +// { // ... / 8L = ... >> 3 +// ____.ldc2_w(8L) +// .ldiv().__(), +// +// ____.iconst_3() +// .lshr().__() +// }, +// { // ... / 16L = ... >> 4 +// ____.ldc2_w(16L) +// .ldiv().__(), +// +// ____.bipush(4) +// .lshr().__() +// }, +// { // ... / 32L = ... >> 5 +// ____.ldc2_w(32L) +// .ldiv().__(), +// +// ____.bipush(5) +// .lshr().__() +// }, +// { // ... / 64L = ... >> 6 +// ____.ldc2_w(64L) +// .ldiv().__(), +// +// ____.bipush(6) +// .lshr().__() +// }, +// { // ... / 128L = ... >> 7 +// ____.ldc2_w(128L) +// .ldiv().__(), +// +// ____.bipush(7) +// .lshr().__() +// }, +// { // ... / 256L = ... >> 8 +// ____.ldc2_w(256L) +// .ldiv().__(), +// +// ____.bipush(8) +// .lshr().__() +// }, +// { // ... / 512L = ... >> 9 +// ____.ldc2_w(512L) +// .ldiv().__(), +// +// ____.bipush(9) +// .lshr().__() +// }, +// { // ... / 1024L = ... >> 10 +// ____.ldc2_w(1024L) +// .ldiv().__(), +// +// ____.bipush(10) +// .lshr().__() +// }, +// { // ... / 2048L = ... >> 11 +// ____.ldc2_w(2048L) +// .ldiv().__(), +// +// ____.bipush(11) +// .lshr().__() +// }, +// { // ... / 4096L = ... >> 12 +// ____.ldc2_w(4096L) +// .ldiv().__(), +// +// ____.bipush(12) +// .lshr().__() +// }, +// { // ... / 8192L = ... >> 13 +// ____.ldc2_w(8192L) +// .ldiv().__(), +// +// ____.bipush(13) +// .lshr().__() +// }, +// { // ... / 16384L = ... >> 14 +// ____.ldc2_w(16384L) +// .ldiv().__(), +// +// ____.bipush(14) +// .lshr().__() +// }, +// { // ... / 32768L = ... >> 15 +// ____.ldc2_w(32768L) +// .ldiv().__(), +// +// ____.bipush(15) +// .lshr().__() +// }, +// { // ... / 65536LL = ... >> 16 +// ____.ldc2_w(65536L) +// .ldiv().__(), +// +// ____.bipush(16) +// .lshr().__() +// }, +// { // ... / 16777216L = ... >> 24 +// ____.ldc2_w(16777216L) +// .ldiv().__(), +// +// ____.bipush(24) +// .lshr().__() +// }, +// { // ... / 4294967296L = ... >> 32 +// ____.ldc2_w(4294967296L) +// .ldiv().__(), +// +// ____.bipush(32) +// .lshr().__() +// }, + { // ... / -1f = -... + ____.ldc(-1f) + .fdiv().__(), + + ____.fneg().__() + }, + { // ... / 1f = ... + ____.fconst_1() + .fdiv().__(), + }, + { // ... / -1d = -... + ____.ldc2_w(-1.) + .ddiv().__(), + + ____.dneg().__() + }, + { // ... / 1d = ... + ____.dconst_1() + .ddiv().__(), + }, + { // ... % 1 = 0 + ____.iconst_1() + .irem().__(), + + ____.pop() + .iconst_0().__() + }, + // Not valid for negative values. +// { // ... % 2 = ... & 0x1 +// ____.iconst_2() +// .irem().__(), +// +// ____.iconst_1() +// .iand().__() +// }, +// { // ... % 4 = ... & 0x3 +// ____.iconst_4() +// .irem().__(), +// +// ____.iconst_3() +// .iand().__() +// }, +// { // ... % 8 = ... & 0x07 +// ____.bipush(8) +// .irem().__(), +// +// ____.bipush(0x07) +// .iand().__() +// }, +// { // ... % 16 = ... & 0x0f +// ____.bipush(16) +// .irem().__(), +// +// ____.bipush(0x0f) +// .iand().__() +// }, +// { // ... % 32 = ... & 0x1f +// ____.bipush(32) +// .irem().__(), +// +// ____.bipush(0x1f) +// .iand().__() +// }, +// { // ... % 64 = ... & 0x3f +// ____.bipush(64) +// .irem().__(), +// +// ____.bipush(0x3f) +// .iand().__() +// }, +// { // ... % 128 = ... & 0x7f +// ____.sipush(128) +// .irem().__(), +// +// ____.bipush(0x7f) +// .iand().__() +// }, +// { // ... % 256 = ... & 0x00ff +// ____.sipush(256) +// .irem().__(), +// +// ____.sipush(0x00ff) +// .iand().__() +// }, +// { // ... % 512 = ... & 0x01ff +// ____.sipush(512) +// .irem().__(), +// +// ____.sipush(0x01ff) +// .iand().__() +// }, +// { // ... % 1024 = ... & 0x03ff +// ____.sipush(1024) +// .irem().__(), +// +// ____.sipush(0x03ff) +// .iand().__() +// }, +// { // ... % 2048 = ... & 0x07ff +// ____.sipush(2048) +// .irem().__(), +// +// ____.sipush(0x07ff) +// .iand().__() +// }, +// { // ... % 4096 = ... & 0x0fff +// ____.sipush(4096) +// .irem().__(), +// +// ____.sipush(0x0fff) +// .iand().__() +// }, +// { // ... % 8192 = ... & 0x1fff +// ____.sipush(8192) +// .irem().__(), +// +// ____.sipush(0x1fff) +// .iand().__() +// }, +// { // ... % 16384 = ... & 0x3fff +// ____.sipush(16384) +// .irem().__(), +// +// ____.sipush(0x3fff) +// .iand().__() +// }, + { // ... % 1L = 0L + ____.lconst_1() + .lrem().__(), + + ____.pop2() + .lconst_0().__() + }, +// { // ... % 1f = 0f +// ____.fconst_1() +// .frem().__(), +// +// ____.pop() +// .fconst_0().__() +// }, +// { // ... % 1d = 0d +// ____.dconst_1() +// .drem().__(), +// +// ____.pop2() +// .dconst_0().__() +// }, + { // -(-...) = ... + ____.ineg() + .ineg().__(), + }, + { // -(-...) = ... + ____.lneg() + .lneg().__(), + }, + { // -(-...) = ... + ____.fneg() + .fneg().__(), + }, + { // -(-...) = ... + ____.dneg() + .dneg().__(), + }, + { // +(-...) = -... + ____.ineg() + .iadd().__(), + + ____.isub().__() + }, + { // +(-...) = -... + ____.lneg() + .ladd().__(), + + ____.lsub().__() + }, + { // +(-...) = -... + ____.fneg() + .fadd().__(), + + ____.fsub().__() + }, + { // +(-...) = -... + ____.dneg() + .dadd().__(), + + ____.dsub().__() + }, + { // ... << 0 = ... + ____.iconst_0() + .ishl().__(), + }, + { // ... << 0 = ... + ____.iconst_0() + .lshl().__(), + }, + { // ... >> 0 = ... + ____.iconst_0() + .ishr().__(), + }, + { // ... >> 0 = ... + ____.iconst_0() + .lshr().__(), + }, + { // ... >>> 0 = ... + ____.iconst_0() + .iushr().__(), + }, + { // ... >>> 0 = ... + ____.iconst_0() + .lushr().__(), + }, + { // ... & -1 = ... + ____.iconst_m1() + .iand().__(), + }, + { // ... & 0 = 0 + ____.iconst_0() + .iand().__(), + + ____.pop() + .iconst_0().__() + }, + { // ... & -1L = ... + ____.ldc2_w(-1L) + .land().__(), + }, + { // ... & 0L = 0L + ____.lconst_0() + .land().__(), + + ____.pop2() + .lconst_0().__() + }, + { // ... | -1 = -1 + ____.iconst_m1() + .ior().__(), + + ____.pop() + .iconst_m1().__() + }, + { // ... | 0 = ... + ____.iconst_0() + .ior().__(), + }, + { // ... | -1L = -1L + ____.ldc2_w(-1L) + .land().__(), + + ____.pop2() + .ldc2_w(-1L).__() + }, + { // ... | 0L = ... + ____.lconst_0() + .lor().__(), + }, + { // ... ^ 0 = ... + ____.iconst_0() + .ixor().__(), + }, + { // ... ^ 0L = ... + ____.lconst_0() + .lxor().__(), + }, + { // (... & 0x0000ff00) >> 8 = (... >> 8) & 0xff + ____.ldc(0x0000ff00) + .iand() + .bipush(8) + .ishr().__(), + + ____.bipush(8) + .ishr() + .sipush(0xff) + .iand().__() + }, + { // (... & 0x0000ff00) >>> 8 = (... >>> 8) & 0xff + ____.ldc(0x0000ff00) + .iand() + .bipush(8) + .iushr().__(), + + ____.bipush(8) + .iushr() + .sipush(0xff) + .iand().__() + }, + { // (... & 0x00ff0000) >> 16 = (... >> 16) & 0xff + ____.ldc(0x00ff0000) + .iand() + .bipush(16) + .ishr().__(), + + ____.bipush(16) + .ishr() + .sipush(0xff) + .iand().__() + }, + { // (... & 0x00ff0000) >>> 16 = (... >>> 16) & 0xff + ____.ldc(0x00ff0000) + .iand() + .bipush(16) + .iushr().__(), + + ____.bipush(16) + .iushr() + .sipush(0xff) + .iand().__() + }, + { // (... & 0xff000000) >> 24 = ... >> 24 + ____.ldc(0xff000000) + .iand() + .bipush(24) + .ishr().__(), + + ____.bipush(24) + .ishr().__() + }, + { // (... & 0xffff0000) >> 16 = ... >> 16 + ____.ldc(0xffff0000) + .iand() + .bipush(16) + .ishr().__(), + + ____.bipush(16) + .ishr().__() + }, + { // (... & 0xffff0000) >>> 16 = ... >>> 16 + ____.ldc(0xffff0000) + .iand() + .bipush(16) + .iushr().__(), + + ____.bipush(16) + .iushr().__() + }, + { // (... >> 24) & 0xff = ... >>> 24 + ____.bipush(24) + .ishr() + .sipush(0xff) + .iand().__(), + + ____.bipush(24) + .iushr().__() + }, + { // (... >>> 24) & 0xff = ... >>> 24 + ____.bipush(24) + .iushr() + .sipush(0xff) + .iand().__(), + + ____.bipush(24) + .iushr().__() + }, + { // (byte)(... & 0x000000ff) = (byte)... + ____.sipush(0xff) + .iand() + .i2b().__(), + + ____.i2b().__() + }, + { // (char)(... & 0x0000ffff) = (char)... + ____.ldc(0x0000ffff) + .iand() + .i2c().__(), + + ____.i2c().__() + }, + { // (short)(... & 0x0000ffff) = (short)... + ____.ldc(0x0000ffff) + .iand() + .i2s().__(), + + ____.i2s().__() + }, + // The Dalvik VM on Android 4.4 throws a VFY error or crashes if + // the byte/short cast is removed before an array store. +// { // (byte)(... >> 24) = ... >> 24 +// ____.bipush(24) +// .ishr() +// .i2b().__(), +// +// ____.bipush(24) +// .ishr().__() +// }, +// { // (byte)(... >>> 24) = ... >> 24 +// ____.bipush(24) +// .iushr() +// .i2b().__(), +// +// ____.bipush(24) +// .ishr().__() +// }, +// { // (char)(... >> 16) = ... >>> 16 +// ____.bipush(16) +// .ishr() +// .i2c().__(), +// +// ____.bipush(16) +// .iushr().__() +// }, +// { // (char)(... >>> 16) = ... >>> 16 +// ____.bipush(16) +// .iushr() +// .i2c().__(), +// +// ____.bipush(16) +// .iushr().__() +// }, +// { // (short)(... >> 16) = ... >> 16 +// ____.bipush(16) +// .ishr() +// .i2s().__(), +// +// ____.bipush(16) +// .ishr().__() +// }, +// { // (short)(... >>> 16) = ... >> 16 +// ____.bipush(16) +// .iushr() +// .i2s().__(), +// +// ____.bipush(16) +// .ishr().__() +// }, + { // ... << 24 >> 24 = (byte)... + ____.bipush(24) + .ishl() + .bipush(24) + .ishr().__(), + + ____.i2b().__() + }, + { // ... << 16 >>> 16 = (char)... + ____.bipush(16) + .ishl() + .bipush(16) + .iushr().__(), + + ____.i2c().__() + }, + { // ... << 16 >> 16 = (short)... + ____.bipush(16) + .ishl() + .bipush(16) + .ishr().__(), + + ____.i2s().__() + }, + { // ... << 32 >> 32 = (long)(int)... + ____.bipush(32) + .lshl() + .bipush(32) + .lshr().__(), + + ____.l2i() + .i2l().__() + }, + { // (int)(... & 0x00000000ffffffffL) = (int)... + ____.ldc2_w(0x00000000ffffffffL) + .land() + .l2i().__(), + + ____.l2i().__() + }, + { // (... & 0xffffffff00000000L) >> 32 = ... >> 32 + ____.ldc2_w(0xffffffff00000000L) + .land() + .bipush(32) + .lshr().__(), + + ____.bipush(32) + .lshr().__() + }, + { // (... & 0xffffffff00000000L) >>> 32 = ... >>> 32 + ____.ldc2_w(0xffffffff00000000L) + .land() + .bipush(32) + .lushr().__(), + + ____.bipush(32) + .lushr().__() + }, + { // ... += 0 = nothing + ____.iinc(X, 0).__(), + }, + }; + + FIELD_SEQUENCES = new Instruction[][][] + { + { // getfield/putfield = nothing + ____.aload(X) + .aload(X) + .getfield(Y) + .putfield(Y).__(), + }, +// { // putfield_L/putfield_L = pop2_x1/putfield +// ____.aload(X) +// // ... +// .putfield(FIELD_J) +// .aload(X) +// // ... +// .putfield(FIELD_J).__(), +// +// ____.aload(X) +// // ... +// .pop2() +// // ... +// .putfield(FIELD_J).__() +// }, +// { // putfield_D/putfield_D = pop2_x1/putfield +// ____.aload(X) +// // ... +// .putfield(FIELD_D) +// .aload(X) +// // ... +// .putfield(FIELD_D).__(), +// +// ____.aload(X) +// // ... +// .pop2() +// // ... +// .putfield(FIELD_D).__() +// }, +// { // putfield/putfield = pop_x1/putfield +// ____.aload(X) +// // ... +// .putfield(Y) +// .aload(X) +// // ... +// .putfield(Y).__(), +// +// ____.aload(X) +// // ... +// .pop() +// // ... +// .putfield(Y).__() +// }, +// { // putfield_L/getfield_L = dup2_x1/putfield +// ____.aload(X) +// // ... +// .putfield(FIELD_J) +// .aload(X) +// .getfield(FIELD_J).__(), +// +// ____.aload(X) +// // ... +// .dup2_x1() +// .putfield(FIELD_J).__() +// }, +// { // putfield_D/getfield_D = dup2_x1/putfield +// ____.aload(X) +// // ... +// .putfield(FIELD_D) +// .aload(X) +// .getfield(FIELD_D).__(), +// +// ____.aload(X) +// // ... +// .dup2_x1() +// .putfield(FIELD_D).__() +// }, +// { // putfield/getfield = dup_x1/putfield +// ____.aload(X) +// // ... +// .putfield(Y) +// .aload(X) +// .getfield(Y).__(), +// +// ____.aload(X) +// // ... +// .dup_x1() +// .putfield(Y).__() +// }, + { // getstatic/putstatic = nothing + ____.getstatic(X) + .putstatic(X).__(), + }, + { // getstatic_L/getstatic_L = getstatic/dup2 + ____.getstatic(FIELD_J) + .getstatic(FIELD_J).__(), + + ____.getstatic(FIELD_J) + .dup2().__() + }, + { // getstatic_D/getstatic_D = getstatic/dup2 + ____.getstatic(FIELD_D) + .getstatic(FIELD_D).__(), + + ____.getstatic(FIELD_D) + .dup2().__() + }, + { // getstatic/getstatic = getstatic/dup + ____.getstatic(X) + .getstatic(X).__(), + + ____.getstatic(X) + .dup().__() + }, + { // putstatic_L/putstatic_L = pop2/putstatic + ____.putstatic(FIELD_J) + .putstatic(FIELD_J).__(), + + ____.pop2() + .putstatic(FIELD_J).__() + }, + { // putstatic_D/putstatic_D = pop2/putstatic + ____.putstatic(FIELD_D) + .putstatic(FIELD_D).__(), + + ____.pop2() + .putstatic(FIELD_D).__() + }, + { // putstatic/putstatic = pop/putstatic + ____.putstatic(X) + .putstatic(X).__(), + + ____.pop() + .putstatic(X).__() + }, + { // putstatic_L/getstatic_L = dup2/putstatic + ____.putstatic(FIELD_J) + .getstatic(FIELD_J).__(), + + ____.dup2() + .putstatic(FIELD_J).__() + }, + { // putstatic_D/getstatic_D = dup2/putstatic + ____.putstatic(FIELD_D) + .getstatic(FIELD_D).__(), + + ____.dup2() + .putstatic(FIELD_D).__() + }, + { // putstatic/getstatic = dup/putstatic + ____.putstatic(X) + .getstatic(X).__(), + + ____.dup() + .putstatic(X).__() + }, + { // L i L: getfield_L/iload/getfield_L = iload/getfield_L/dup2_x1 + ____.aload(A) + .getfield(FIELD_J) + .iload(B) + .aload(A) + .getfield(FIELD_J).__(), + + ____.iload(B) + .aload(A) + .getfield(FIELD_J) + .dup2_x1().__() + }, + { // D i D: getfield_D/iload/getfield_D = iload/getfield_D/dup2_x1 + ____.aload(A) + .getfield(FIELD_D) + .iload(B) + .aload(A) + .getfield(FIELD_D).__(), + + ____.iload(B) + .aload(A) + .getfield(FIELD_D) + .dup2_x1().__() + }, + { // X i X (e.g. X[i] = X[.] ...): getfield/iload/getfield = iload/getfield/dup_x1 + ____.aload(A) + .getfield(X) + .iload(B) + .aload(A) + .getfield(X).__(), + + ____.iload(B) + .aload(A) + .getfield(X) + .dup_x1().__() + }, + { // L i L: getstatic_L/iload/getstatic_L = iload/getstatic_L/dup2_x1 + ____.getstatic(FIELD_J) + .iload(A) + .getstatic(FIELD_J).__(), + + ____.iload(A) + .getstatic(FIELD_J) + .dup2_x1().__() + }, + { // D i D: getstatic_D/iload/getstatic_D = iload/getstatic_D/dup2_x1 + ____.getstatic(FIELD_D) + .iload(A) + .getstatic(FIELD_D).__(), + + ____.iload(A) + .getstatic(FIELD_D) + .dup2_x1().__() + }, + { // X i X (e.g. X[i] = X[.] ...): getstatic/iload/getstatic = iload/getstatic/dup_x1 + ____.getstatic(X) + .iload(A) + .getstatic(X).__(), + + ____.iload(A) + .getstatic(X) + .dup_x1().__() + }, + { // X[i] j X[i] (e.g. X[i][j] = X[i][.] ...): getfield/iload/aaload/iload/getfield/iload/aaload = iload/getfield//iload/aaload/iload/dup_x1 + ____.aload(A) + .getfield(X) + .iload(B) + .aaload() + .iload(C) + .aload(A) + .getfield(X) + .iload(B) + .aaload().__(), + + ____.iload(C) + .aload(A) + .getfield(X) + .iload(B) + .aaload() + .dup_x1().__() + }, + { // X[i] j X[i] (e.g. X[i][j] = X[i][.] ...): getstatic/iload/aaload/iload/getstatic/iload/aaload = iload/getstatic//iload/aaload/iload/dup_x1 + ____.getstatic(X) + .iload(B) + .aaload() + .iload(C) + .getstatic(X) + .iload(B) + .aaload().__(), + + ____.iload(C) + .getstatic(X) + .iload(B) + .aaload() + .dup_x1().__() + }, + }; + + CAST_SEQUENCES = new Instruction[][][] + { + { // (byte)(byte)... = (byte)... + ____.i2b() + .i2b().__(), + + ____.i2b().__() + }, + { // (byte)(char)... = (byte)... + ____.i2c() + .i2b().__(), + + ____.i2b().__() + }, + { // (byte)(short)... = (byte)... + ____.i2s() + .i2b().__(), + + ____.i2b().__() + }, + { // (char)(char)... = (char)... + ____.i2c() + .i2c().__(), + + ____.i2c().__() + }, + { // (char)(short)... = (char)... + ____.i2s() + .i2c().__(), + + ____.i2c().__() + }, +// { // (short)(byte)... = (byte)... +// ____.i2b() +// .i2s().__(), +// +// ____.i2b().__() +// }, + { // (short)(char)... = (short)... + ____.i2c() + .i2s().__(), + + ____.i2s().__() + }, + { // (short)(short)... = (short)... + ____.i2s() + .i2s().__(), + + ____.i2s().__() + }, + { // (int)(long)... = ... + ____.i2l() + .l2i().__(), + }, + { // (int)(double)... = ... + ____.i2d() + .d2i().__(), + }, + { // (float)(double)... = (float)... for ints + ____.i2d() + .d2f().__(), + + ____.i2f().__() + }, + { // (float)(double)... = (float)... for longs + ____.l2d() + .d2f().__(), + + ____.l2f().__() + }, + { // (int)(double)... = (int)... + ____.f2d() + .d2i().__(), + + ____.f2i().__() + }, + { // (long)(double)... = (long)... + ____.f2d() + .d2l().__(), + + ____.f2l().__() + }, + { // (X)(X)... = (X)... + ____.checkcast(X) + .checkcast(X).__(), + + ____.checkcast(X).__() + }, + // Not handled correctly in all cases by VMs prior to Java 6... +// { // (byte)bytes[...] = bytes[...] +// ____.baload() +// .i2b().__(), +// +// ____.baload().__() +// }, +// { // (short)bytes[...] = bytes[...] +// ____.baload() +// .i2s().__(), +// +// ____.baload().__() +// }, +// { // (char)chars[...] = chars[...] +// ____.caload() +// .i2c().__(), +// +// ____.caload().__() +// }, +// { // (short)shorts[...] = shorts[...] +// ____.saload() +// .i2s().__(), +// +// ____.saload().__() +// }, +// { // bytes[...] = (byte)... = bytes[...] = ... +// ____.i2b() +// .bastore().__(), +// +// ____.bastore().__() +// }, +// { // chars[...] = (char)... = chars[...] = ... +// ____.i2c() +// .castore().__(), +// +// ____.castore().__() +// }, +// { // shorts[...] = (short)... = shorts[...] = ... +// ____.i2s() +// .sastore().__(), +// +// ____.sastore().__() +// }, + }; + + BRANCH_SEQUENCES = new Instruction[][][] + { + { // goto +3 = nothing + ____.goto_(3).__(), + }, + { // ifeq +3 = pop + ____.ifeq(3).__(), + + ____.pop().__() + }, + { // ifne +3 = pop + ____.ifne(3).__(), + + ____.pop().__() + }, + { // iflt +3 = pop + ____.iflt(3).__(), + + ____.pop().__() + }, + { // ifge +3 = pop + ____.ifge(3).__(), + + ____.pop().__() + }, + { // ifgt +3 = pop + ____.ifgt(3).__(), + + ____.pop().__() + }, + { // ifle +3 = pop + ____.ifle(3).__(), + + ____.pop().__() + }, + { // ificmpeq +3 = pop2 + ____.ificmpeq(3).__(), + + ____.pop2().__() + }, + { // ificmpne +3 = pop2 + ____.ificmpne(3).__(), + + ____.pop2().__() + }, + { // ificmplt +3 = pop2 + ____.ificmplt(3).__(), + + ____.pop2().__() + }, + { // ificmpge +3 = pop2 + ____.ificmpge(3).__(), + + ____.pop2().__() + }, + { // ificmpgt +3 = pop2 + ____.ificmpgt(3).__(), + + ____.pop2().__() + }, + { // ificmple +3 = pop2 + ____.ificmple(3).__(), + + ____.pop2().__() + }, + { // ifacmpeq +3 = pop2 + ____.ifacmpeq(3).__(), + + ____.pop2().__() + }, + { // ifacmpne +3 = pop2 + ____.ifacmpne(3).__(), + + ____.pop2().__() + }, + { // ifnull +3 = pop + ____.ifnull(3).__(), + + ____.pop().__() + }, + { // ifnonnull +3 = pop + ____.ifnonnull(3).__(), + + ____.pop().__() + }, + { // if (... == 0) = ifeq + ____.iconst_0() + .ificmpeq(X).__(), + + ____.ifeq(X).__() + }, + { // if (0 == i) = iload/ifeq + ____.iconst_0() + .iload(Y) + .ificmpeq(X).__(), + + ____.iload(Y) + .ifeq(X).__() + }, + { // if (0 == i) = getstatic/ifeq + ____.iconst_0() + .getstatic(Y) + .ificmpeq(X).__(), + + ____.getstatic(Y) + .ifeq(X).__() + }, + { // if (0 == i) = getfield/ifeq + ____.iconst_0() + .aload(Y) + .getfield(Z) + .ificmpeq(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifeq(X).__() + }, + { // if (... != 0) = ifne + ____.iconst_0() + .ificmpne(X).__(), + + ____.ifne(X).__() + }, + { // if (0 != i) = iload/ifeq + ____.iconst_0() + .iload(Y) + .ificmpne(X).__(), + + ____.iload(Y) + .ifne(X).__() + }, + { // if (0 != i) = getstatic/ifeq + ____.iconst_0() + .getstatic(Y) + .ificmpne(X).__(), + + ____.getstatic(Y) + .ifne(X).__() + }, + { // if (0 != i) = getfield/ifeq + ____.iconst_0() + .aload(Y) + .getfield(Z) + .ificmpne(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifne(X).__() + }, + { // if (... < 0) = iflt + ____.iconst_0() + .ificmplt(X).__(), + + ____.iflt(X).__() + }, + { // if (... < 1) = ifle + ____.iconst_1() + .ificmplt(X).__(), + + ____.ifle(X).__() + }, + { // if (0 > i) = iload/iflt + ____.iconst_0() + .iload(Y) + .ificmpgt(X).__(), + + ____.iload(Y) + .iflt(X).__() + }, + { // if (1 > i) = iload/ifle + ____.iconst_1() + .iload(Y) + .ificmpgt(X).__(), + + ____.iload(Y) + .ifle(X).__() + }, + { // if (0 > i) = getstatic/iflt + ____.iconst_0() + .getstatic(Y) + .ificmpgt(X).__(), + + ____.getstatic(Y) + .iflt(X).__() + }, + { // if (1 > i) = getstatic/ifle + ____.iconst_1() + .getstatic(Y) + .ificmpgt(X).__(), + + ____.getstatic(Y) + .ifle(X).__() + }, + { // if (0 > i) = getfield/iflt + ____.iconst_0() + .aload(Y) + .getfield(Z) + .ificmpgt(X).__(), + + ____.aload(Y) + .getfield(Z) + .iflt(X).__() + }, + { // if (1 > i) = getfield/ifle + ____.iconst_1() + .aload(Y) + .getfield(Z) + .ificmpgt(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifle(X).__() + }, + { // if (... >= 0) = ifge + ____.iconst_0() + .ificmpge(X).__(), + + ____.ifge(X).__() + }, + { // if (... >= 1) = ifgt + ____.iconst_1() + .ificmpge(X).__(), + + ____.ifgt(X).__() + }, + { // if (0 <= i) = iload/ifge + ____.iconst_0() + .iload(Y) + .ificmple(X).__(), + + ____.iload(Y) + .ifge(X).__() + }, + { // if (1 <= i) = iload/ifgt + ____.iconst_1() + .iload(Y) + .ificmple(X).__(), + + ____.iload(Y) + .ifgt(X).__() + }, + { // if (0 <= i) = getstatic/ifge + ____.iconst_0() + .getstatic(Y) + .ificmple(X).__(), + + ____.getstatic(Y) + .ifge(X).__() + }, + { // if (1 <= i) = getstatic/ifgt + ____.iconst_1() + .getstatic(Y) + .ificmple(X).__(), + + ____.getstatic(Y) + .ifgt(X).__() + }, + { // if (0 <= i) = getfield/ifge + ____.iconst_0() + .aload(Y) + .getfield(Z) + .ificmple(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifge(X).__() + }, + { // if (1 <= i) = getfield/ifgt + ____.iconst_1() + .aload(Y) + .getfield(Z) + .ificmple(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifgt(X).__() + }, + { // if (... > 0) = ifgt + ____.iconst_0() + .ificmpgt(X).__(), + + ____.ifgt(X).__() + }, + { // if (... > -1) = ifge + ____.iconst_m1() + .ificmpgt(X).__(), + + ____.ifge(X).__() + }, + { // if (0 < i) = iload/ifgt + ____.iconst_0() + .iload(Y) + .ificmplt(X).__(), + + ____.iload(Y) + .ifgt(X).__() + }, + { // if (-1 < i) = iload/ifge + ____.iconst_m1() + .iload(Y) + .ificmplt(X).__(), + + ____.iload(Y) + .ifge(X).__() + }, + { // if (0 < i) = getstatic/ifgt + ____.iconst_0() + .getstatic(Y) + .ificmplt(X).__(), + + ____.getstatic(Y) + .ifgt(X).__() + }, + { // if (-1 < i) = getstatic/ifge + ____.iconst_m1() + .getstatic(Y) + .ificmplt(X).__(), + + ____.getstatic(Y) + .ifge(X).__() + }, + { // if (0 < i) = getfield/ifgt + ____.iconst_0() + .aload(Y) + .getfield(Z) + .ificmplt(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifgt(X).__() + }, + { // if (-1 < i) = getfield/ifge + ____.iconst_m1() + .aload(Y) + .getfield(Z) + .ificmplt(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifge(X).__() + }, + { // if (... <= 0) = ifle + ____.iconst_0() + .ificmple(X).__(), + + ____.ifle(X).__() + }, + { // if (... <= -1) = iflt + ____.iconst_m1() + .ificmple(X).__(), + + ____.iflt(X).__() + }, + { // if (0 >= i) = iload/ifle + ____.iconst_0() + .iload(Y) + .ificmpge(X).__(), + + ____.iload(Y) + .ifle(X).__() + }, + { // if (-1 >= i) = iload/iflt + ____.iconst_m1() + .iload(Y) + .ificmpge(X).__(), + + ____.iload(Y) + .iflt(X).__() + }, + { // if (0 >= i) = getstatic/ifle + ____.iconst_0() + .getstatic(Y) + .ificmpge(X).__(), + + ____.getstatic(Y) + .ifle(X).__() + }, + { // if (-1 >= i) = getstatic/iflt + ____.iconst_m1() + .getstatic(Y) + .ificmpge(X).__(), + + ____.getstatic(Y) + .iflt(X).__() + }, + { // if (0 >= i) = getfield/ifle + ____.iconst_0() + .aload(Y) + .getfield(Z) + .ificmpge(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifle(X).__() + }, + { // if (-1 >= i) = getfield/iflt + ____.iconst_m1() + .aload(Y) + .getfield(Z) + .ificmpge(X).__(), + + ____.aload(Y) + .getfield(Z) + .iflt(X).__() + }, + { // if (... == null) = ifnull + ____.aconst_null() + .ifacmpeq(X).__(), + + ____.ifnull(X).__() + }, + { // if (null == a) = aload/ifnull + ____.aconst_null() + .aload(Y) + .ifacmpeq(X).__(), + + ____.aload(Y) + .ifnull(X).__() + }, + { // if (null == a) = getstatic/ifnull + ____.aconst_null() + .getstatic(Y) + .ifacmpeq(X).__(), + + ____.getstatic(Y) + .ifnull(X).__() + }, + { // if (null == a) = getfield/ifnull + ____.aconst_null() + .aload(Y) + .getfield(Z) + .ifacmpeq(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifnull(X).__() + }, + { // if (... != null) = ifnonnull + ____.aconst_null() + .ifacmpne(X).__(), + + ____.ifnonnull(X).__() + }, + { // if (null != a) = aload/ifnonnull + ____.aconst_null() + .aload(Y) + .ifacmpne(X).__(), + + ____.aload(Y) + .ifnonnull(X).__() + }, + { // if (null != a) = getstatic/ifnonnull + ____.aconst_null() + .getstatic(Y) + .ifacmpne(X).__(), + + ____.getstatic(Y) + .ifnonnull(X).__() + }, + { // if (null != a) = getfield/ifnonnull + ____.aconst_null() + .aload(Y) + .getfield(Z) + .ifacmpne(X).__(), + + ____.aload(Y) + .getfield(Z) + .ifnonnull(X).__() + }, + { // iconst_0/ifeq = goto + ____.iconst_0() + .ifeq(X).__(), + + ____.goto_(X).__() + }, + { // iconst/ifeq = nothing + ____.iconst(A) + .ifeq(X).__(), + }, + { // bipush/ifeq = nothing + ____.bipush(A) + .ifeq(X).__(), + }, + { // sipush/ifeq = nothing + ____.sipush(A) + .ifeq(X).__(), + }, + { // iconst_0/ifne = nothing + ____.iconst_0() + .ifne(X).__(), + }, + { // iconst/ifne = goto + ____.iconst(A) + .ifne(X).__(), + + ____.goto_(X).__() + }, + { // bipush/ifne = goto + ____.bipush(A) + .ifne(X).__(), + + ____.goto_(X).__() + }, + { // sipush/ifne = goto + ____.sipush(A) + .ifne(X).__(), + + ____.goto_(X).__() + }, + { // iconst_0/iflt = nothing + ____.iconst_0() + .iflt(X).__(), + }, + { // iconst_0/ifge = goto + ____.iconst_0() + .ifge(X).__(), + + ____.goto_(X).__() + }, + { // iconst_0/ifgt = nothing + ____.iconst_0() + .ifgt(X).__(), + }, + { // iconst_0/ifle = goto + ____.iconst_0() + .ifle(X).__(), + + ____.goto_(X).__() + }, + { // aconst_null/ifnull = goto + ____.aconst_null() + .ifnull(X).__(), + + ____.goto_(X).__() + }, + { // aconst_null/ifnonnul = nothing + ____.aconst_null() + .ifnonnull(X).__(), + }, + { // ifeq/goto = ifne + ____.ifeq(6) + .goto_(X).__(), + + ____.ifne(X).__() + }, + { // ifne/goto = ifeq + ____.ifne(6) + .goto_(X).__(), + + ____.ifeq(X).__() + }, + { // iflt/goto = ifge + ____.iflt(6) + .goto_(X).__(), + + ____.ifge(X).__() + }, + { // ifge/goto = iflt + ____.ifge(6) + .goto_(X).__(), + + ____.iflt(X).__() + }, + { // ifgt/goto = ifle + ____.ifgt(6) + .goto_(X).__(), + + ____.ifle(X).__() + }, + { // ifle/goto = ifgt + ____.ifle(6) + .goto_(X).__(), + + ____.ifgt(X).__() + }, + { // ificmpeq/goto = ificmpne + ____.ificmpeq(6) + .goto_(X).__(), + + ____.ificmpne(X).__() + }, + { // ificmpne/goto = ificmpeq + ____.ificmpne(6) + .goto_(X).__(), + + ____.ificmpeq(X).__() + }, + { // ificmplt/goto = ificmpge + ____.ificmplt(6) + .goto_(X).__(), + + ____.ificmpge(X).__() + }, + { // ificmpge/goto = ificmplt + ____.ificmpge(6) + .goto_(X).__(), + + ____.ificmplt(X).__() + }, + { // ificmpgt/goto = ificmple + ____.ificmpgt(6) + .goto_(X).__(), + + ____.ificmple(X).__() + }, + { // ificmple/goto = ificmpgt + ____.ificmple(6) + .goto_(X).__(), + + ____.ificmpgt(X).__() + }, + { // ifacmpeq/goto = ifacmpne + ____.ifacmpeq(6) + .goto_(X).__(), + + ____.ifacmpne(X).__() + }, + { // ifacmpne/goto = ifacmpeq + ____.ifacmpne(6) + .goto_(X).__(), + + ____.ifacmpeq(X).__() + }, + { // ifnull/goto = ifnonnull + ____.ifnull(6) + .goto_(X).__(), + + ____.ifnonnull(X).__() + }, + { // ifnonnull/goto = ifnull + ____.ifnonnull(6) + .goto_(X).__(), + + ____.ifnull(X).__() + }, +// { // switch (...) { default: ... } = pop/goto ... +// ____.tableswitch(A, X, Y, 0, new int[0]).__(), +// +// ____.pop() +// .goto_(A).__() +// }, +// { // switch (...) { default: ... } = pop/goto ... +// ____.lookupswitch(A, 0, new int[0], new int[0]).__(), +// +// ____.pop() +// .goto_(A).__() +// }, + { // switch (...) { case/case/default: ... } = switch (...) { case/default: ... } + ____.lookupswitch(A, new int[] { X, Y }, new int[] { A, B }).__(), + + ____.lookupswitch(A, new int[] { Y }, new int[] { B }).__() + }, + { // switch (...) { case/case/default: ... } = switch (...) { case/default: ... } + ____.lookupswitch(B, new int[] { X, Y }, new int[] { A, B }).__(), + + ____.lookupswitch(B, new int[] { X }, new int[] { A }).__() + }, + { // switch (...) { case/case/case/default: ... } = switch (...) { case/case/default: ... } + ____.lookupswitch(A, new int[] { X, Y, Z }, new int[] { A, B, C }).__(), + + ____.lookupswitch(A, new int[] { Y, Z }, new int[] { B, C }).__() + }, + { // switch (...) { case/case/case/default: ... } = switch (...) { case/case/default: ... } + ____.lookupswitch(B, new int[] { X, Y, Z }, new int[] { A, B, C }).__(), + + ____.lookupswitch(B, new int[] { X, Z }, new int[] { A, C }).__() + }, + { // switch (...) { case/case/case/default: ... } = switch (...) { case/case/default: ... } + ____.lookupswitch(C, new int[] { X, Y, Z }, new int[] { A, B, C }).__(), + + ____.lookupswitch(C, new int[] { X, Y }, new int[] { A, B }).__() + }, +// { // switch (...) { case ...: ... default: ... } +// // = if (... == ...) ... else ... +// ____.tableswitch(A, X, Y, 1, new int[] { B }).__(), +// +// ____.sipush(X) +// .ificmpne(A) +// .goto_(B).__() +// }, +// { // switch (...) { case ...: ... default: ... } +// // = if (... == ...) ... else ... +// ____.lookupswitch(A, 1, new int[] { X }, new int[] { B }).__(), +// +// ____.sipush(X) +// .ificmpne(A) +// .goto_(B).__() +// } + }; + + OBJECT_SEQUENCES = new Instruction[][][] + { + { // "...".equals("...") = X.class.equals(X.class) = true (ignoring class loader) + ____.ldc_(A) + .ldc_(A) + .invokevirtual(EQUALS).__(), + + ____.iconst_1().__() + }, + { // ....equals(dup) = true (discarding any NullPointerException) + ____.dup() + .invokevirtual(EQUALS).__(), + + ____.pop() + .iconst_1().__() + }, + { // object.equals(object) = true (ignoring implementation and discarding any NullPointerException) + ____.aload(A) + .aload(A) + .invokevirtual(EQUALS).__(), + + ____.iconst_1().__() + }, + { // object.equals(object) = true (ignoring implementation and discarding any NullPointerException) + ____.getstatic(A) + .getstatic(A) + .invokevirtual(EQUALS).__(), + + ____.iconst_1().__() + }, + { // object.equals(object) = true (ignoring implementation and discarding any NullPointerException) + ____.aload(A) + .getfield(B) + .aload(A) + .getfield(B) + .invokevirtual(EQUALS).__(), + + ____.iconst_1().__() + }, + { // Boolean.valueOf(false) = Boolean.FALSE + ____.iconst_0() + .invokestatic(BOOLEAN, "valueOf", "(Z)Ljava/lang/Boolean;").__(), + + ____.getstatic(BOOLEAN, "FALSE", "Ljava/lang/Boolean;").__() + }, + { // Boolean.valueOf(true) = Boolean.TRUE + ____.iconst_1() + .invokestatic(BOOLEAN, "valueOf", "(Z)Ljava/lang/Boolean;").__(), + + ____.getstatic(BOOLEAN, "TRUE", "Ljava/lang/Boolean;").__() + }, + { // new Boolean(false) = Boolean.FALSE (ignoring identity) + ____.new_(BOOLEAN) + .dup() + .iconst_0() + .invokespecial(BOOLEAN, "", "(Z)V").__(), + + ____.getstatic(BOOLEAN, "FALSE", "Ljava/lang/Boolean;").__() + }, + { // new Boolean(true) = Boolean.TRUE (ignoring identity) + ____.new_(BOOLEAN) + .dup() + .iconst_1() + .invokespecial(BOOLEAN, "", "(Z)V").__(), + + ____.getstatic(BOOLEAN, "TRUE", "Ljava/lang/Boolean;").__() + }, + { // new Boolean(v) = Boolean.valueof(v) (ignoring identity) + ____.new_(BOOLEAN) + .dup() + .iload(A) + .invokespecial(BOOLEAN, "", "(Z)V").__(), + + ____.iload(A) + .invokestatic(BOOLEAN, "valueOf", "(Z)Ljava/lang/Boolean;").__() + }, + { // new Boolean(s) = Boolean.valueof(s) (ignoring identity) + ____.new_(BOOLEAN) + .dup() + .getstatic(FIELD_Z) + .invokespecial(BOOLEAN, "", "(Z)V").__(), + + ____.getstatic(FIELD_Z) + .invokestatic(BOOLEAN, "valueOf", "(Z)Ljava/lang/Boolean;").__() + }, + { // new Boolean(v.f) = Boolean.valueof(v.f) (ignoring identity) + ____.new_(BOOLEAN) + .dup() + .aload(A) + .getfield(FIELD_Z) + .invokespecial(BOOLEAN, "", "(Z)V").__(), + + ____.aload(A) + .getfield(FIELD_Z) + .invokestatic(BOOLEAN, "valueOf", "(Z)Ljava/lang/Boolean;").__() + }, + { // Boolean.FALSE.booleanValue() = false + ____.getstatic(BOOLEAN, "FALSE", "Ljava/lang/Boolean;") + .invokevirtual(BOOLEAN_VALUE).__(), + + ____.iconst_0().__() + }, + { // Boolean.TRUE.booleanValue() = true + ____.getstatic(BOOLEAN, "TRUE", "Ljava/lang/Boolean;") + .invokevirtual(BOOLEAN_VALUE).__(), + + ____.iconst_1().__() + }, + { // Boolean.valueOf(...).booleanValue() = nothing + ____.invokestatic(BOOLEAN, "valueOf", "(Z)Ljava/lang/Boolean;") + .invokevirtual(BOOLEAN_VALUE).__(), + }, + { // new Byte(B) = Byte.valueof(B) (ignoring identity) + ____.new_(BYTE) + .dup() + .iconst(A) + .invokespecial(BYTE, "", "(B)V").__(), + + ____.iconst(A) + .invokestatic(BYTE, "valueOf", "(B)Ljava/lang/Byte;").__() + }, + { // new Byte(v) = Byte.valueof(v) (ignoring identity) + ____.new_(BYTE) + .dup() + .iload(A) + .invokespecial(BYTE, "", "(B)V").__(), + + ____.iload(A) + .invokestatic(BYTE, "valueOf", "(B)Ljava/lang/Byte;").__() + }, + { // new Byte(s) = Byte.valueof(s) (ignoring identity) + ____.new_(BYTE) + .dup() + .getstatic(FIELD_B) + .invokespecial(BYTE, "", "(B)V").__(), + + ____.getstatic(FIELD_B) + .invokestatic(BYTE, "valueOf", "(B)Ljava/lang/Byte;").__() + }, + { // new Byte(v.f) = Byte.valueof(v.f) (ignoring identity) + ____.new_(BYTE) + .dup() + .aload(A) + .getfield(FIELD_B) + .invokespecial(BYTE, "", "(B)V").__(), + + ____.aload(A) + .getfield(FIELD_B) + .invokestatic(BYTE, "valueOf", "(B)Ljava/lang/Byte;").__() + }, + { // Byte.valueOf(...).byteValue() = nothing + ____.invokestatic(BYTE, "valueOf", "(B)Ljava/lang/Byte;") + .invokevirtual(BYTE_VALUE).__(), + }, + { // new Character(C) = Character.valueof(C) (ignoring identity) + ____.new_(CHARACTER) + .dup() + .iconst(A) + .invokespecial(CHARACTER, "", "(C)V").__(), + + ____.iconst(A) + .invokestatic(CHARACTER, "valueOf", "(C)Ljava/lang/Character;").__() + }, + { // new Character(v) = Character.valueof(v) (ignoring identity) + ____.new_(CHARACTER) + .dup() + .iload(A) + .invokespecial(CHARACTER, "", "(C)V").__(), + + ____.iload(A) + .invokestatic(CHARACTER, "valueOf", "(C)Ljava/lang/Character;").__() + }, + { // new Character(s) = Character.valueof(s) (ignoring identity) + ____.new_(CHARACTER) + .dup() + .getstatic(FIELD_C) + .invokespecial(CHARACTER, "", "(C)V").__(), + + ____.getstatic(FIELD_C) + .invokestatic(CHARACTER, "valueOf", "(C)Ljava/lang/Character;").__() + }, + { // new Character(v.f) = Character.valueof(v.f) (ignoring identity) + ____.new_(CHARACTER) + .dup() + .aload(A) + .getfield(FIELD_C) + .invokespecial(CHARACTER, "", "(C)V").__(), + + ____.aload(A) + .getfield(FIELD_C) + .invokestatic(CHARACTER, "valueOf", "(C)Ljava/lang/Character;").__() + }, + { // Character.valueOf(...).charValue() = nothing + ____.invokestatic(CHARACTER, "valueOf", "(C)Ljava/lang/Character;") + .invokevirtual(CHAR_VALUE).__(), + }, + { // new Short(S) = Short.valueof(S) (ignoring identity) + ____.new_(SHORT) + .dup() + .iconst(A) + .invokespecial(SHORT, "", "(S)V").__(), + + ____.iconst(A) + .invokestatic(SHORT, "valueOf", "(S)Ljava/lang/Short;").__() + }, + { // new Short(v) = Short.valueof(v) (ignoring identity) + ____.new_(SHORT) + .dup() + .iload(A) + .invokespecial(SHORT, "", "(S)V").__(), + + ____.iload(A) + .invokestatic(SHORT, "valueOf", "(S)Ljava/lang/Short;").__() + }, + { // new Short(s) = Short.valueof(s) (ignoring identity) + ____.new_(SHORT) + .dup() + .getstatic(FIELD_S) + .invokespecial(SHORT, "", "(S)V").__(), + + ____.getstatic(FIELD_S) + .invokestatic(SHORT, "valueOf", "(S)Ljava/lang/Short;").__() + }, + { // new Short(v.f) = Short.valueof(v.f) (ignoring identity) + ____.new_(SHORT) + .dup() + .aload(A) + .getfield(FIELD_S) + .invokespecial(SHORT, "", "(S)V").__(), + + ____.aload(A) + .getfield(FIELD_S) + .invokestatic(SHORT, "valueOf", "(S)Ljava/lang/Short;").__() + }, + { // Short.valueOf(...).shortValue() = nothing + ____.invokestatic(SHORT, "valueOf", "(S)Ljava/lang/Short;") + .invokevirtual(SHORT_VALUE).__(), + }, + { // new Integer(I) = Integer.valueof(I) (ignoring identity) + ____.new_(INTEGER) + .dup() + .iconst(A) + .invokespecial(INTEGER, "", "(I)V").__(), + + ____.iconst(A) + .invokestatic(INTEGER, "valueOf", "(I)Ljava/lang/Integer;").__() + }, + { // new Integer(I) = Integer.valueof(I) (ignoring identity) + ____.new_(INTEGER) + .dup() + .ldc_(A) + .invokespecial(INTEGER, "", "(I)V").__(), + + ____.ldc_(A) + .invokestatic(INTEGER, "valueOf", "(I)Ljava/lang/Integer;").__() + }, + { // new Integer(v) = Integer.valueof(v) (ignoring identity) + ____.new_(INTEGER) + .dup() + .iload(A) + .invokespecial(INTEGER, "", "(I)V").__(), + + ____.iload(A) + .invokestatic(INTEGER, "valueOf", "(I)Ljava/lang/Integer;").__() + }, + { // new Integer(c) = Integer.valueof(c) (ignoring identity) + ____.new_(INTEGER) + .dup() + .getstatic(FIELD_I) + .invokespecial(INTEGER, "", "(I)V").__(), + + ____.getstatic(FIELD_I) + .invokestatic(INTEGER, "valueOf", "(I)Ljava/lang/Integer;").__() + }, + { // new Integer(v.f) = Integer.valueof(v.f) (ignoring identity) + ____.new_(INTEGER) + .dup() + .aload(A) + .getfield(FIELD_I) + .invokespecial(INTEGER, "", "(I)V").__(), + + ____.aload(A) + .getfield(FIELD_I) + .invokestatic(INTEGER, "valueOf", "(I)Ljava/lang/Integer;").__() + }, + { // Integer.valueOf(...).intValue() = nothing + ____.invokestatic(INTEGER, "valueOf", "(I)Ljava/lang/Integer;") + .invokevirtual(INT_VALUE).__(), + }, + { // new Float(F) = Float.valueof(F) (ignoring identity) + ____.new_(FLOAT) + .dup() + .fconst(A) + .invokespecial(FLOAT, "", "(F)V").__(), + + ____.fconst(A) + .invokestatic(FLOAT, "valueOf", "(F)Ljava/lang/Float;").__() + }, + { // new Float(F) = Float.valueof(F) (ignoring identity) + ____.new_(FLOAT) + .dup() + .ldc_(A) + .invokespecial(FLOAT, "", "(F)V").__(), + + ____.ldc_(A) + .invokestatic(FLOAT, "valueOf", "(F)Ljava/lang/Float;").__() + }, + { // new Float(v) = Float.valueof(v) (ignoring identity) + ____.new_(FLOAT) + .dup() + .fload(A) + .invokespecial(FLOAT, "", "(F)V").__(), + + ____.fload(A) + .invokestatic(FLOAT, "valueOf", "(F)Ljava/lang/Float;").__() + }, + { // new Float(s) = Float.valueof(s) (ignoring identity) + ____.new_(FLOAT) + .dup() + .getstatic(FIELD_F) + .invokespecial(FLOAT, "", "(F)V").__(), + + ____.getstatic(FIELD_F) + .invokestatic(FLOAT, "valueOf", "(F)Ljava/lang/Float;").__() + }, + { // new Float(v.f) = Float.valueof(v.f) (ignoring identity) + ____.new_(FLOAT) + .dup() + .aload(A) + .getfield(FIELD_F) + .invokespecial(FLOAT, "", "(F)V").__(), + + ____.aload(A) + .getfield(FIELD_F) + .invokestatic(FLOAT, "valueOf", "(F)Ljava/lang/Float;").__() + }, + { // Float.valueOf(...).floatValue() = nothing + ____.invokestatic(FLOAT, "valueOf", "(F)Ljava/lang/Float;") + .invokevirtual(FLOAT_VALUE).__(), + }, + { // new Long(J) = Long.valueof(J) (ignoring identity) + ____.new_(LONG) + .dup() + .lconst(A) + .invokespecial(LONG, "", "(J)V").__(), + + ____.lconst(A) + .invokestatic(LONG, "valueOf", "(J)Ljava/lang/Long;").__() + }, + { // new Long(J) = Long.valueof(J) (ignoring identity) + ____.new_(LONG) + .dup() + .ldc2_w(A) + .invokespecial(LONG, "", "(J)V").__(), + + ____.ldc2_w(A) + .invokestatic(LONG, "valueOf", "(J)Ljava/lang/Long;").__() + }, + { // new Long(v) = Long.valueof(v) (ignoring identity) + ____.new_(LONG) + .dup() + .iload(A) + .invokespecial(LONG, "", "(J)V").__(), + + ____.iload(A) + .invokestatic(LONG, "valueOf", "(J)Ljava/lang/Long;").__() + }, + { // new Long(s) = Long.valueof(s) (ignoring identity) + ____.new_(LONG) + .dup() + .getstatic(FIELD_J) + .invokespecial(LONG, "", "(J)V").__(), + + ____.getstatic(FIELD_J) + .invokestatic(LONG, "valueOf", "(J)Ljava/lang/Long;").__() + }, + { // new Long(v.f) = Long.valueof(v.f) (ignoring identity) + ____.new_(LONG) + .dup() + .aload(A) + .getfield(FIELD_J) + .invokespecial(LONG, "", "(J)V").__(), + + ____.aload(A) + .getfield(FIELD_J) + .invokestatic(LONG, "valueOf", "(J)Ljava/lang/Long;").__() + }, + { // Long.valueOf(...).longValue() = nothing + ____.invokestatic(LONG, "valueOf", "(J)Ljava/lang/Long;") + .invokevirtual(LONG_VALUE).__(), + }, + { // new Double(D) = Double.valueof(D) (ignoring identity) + ____.new_(DOUBLE) + .dup() + .dconst(A) + .invokespecial(DOUBLE, "", "(D)V").__(), + + ____.dconst(A) + .invokestatic(DOUBLE, "valueOf", "(D)Ljava/lang/Double;").__() + }, + { // new Double(D) = Double.valueof(D) (ignoring identity) + ____.new_(DOUBLE) + .dup() + .ldc2_w(A) + .invokespecial(DOUBLE, "", "(D)V").__(), + + ____.ldc2_w(A) + .invokestatic(DOUBLE, "valueOf", "(D)Ljava/lang/Double;").__() + }, + { // new Double(v) = Double.valueof(v) (ignoring identity) + ____.new_(DOUBLE) + .dup() + .dload(A) + .invokespecial(DOUBLE, "", "(D)V").__(), + + ____.dload(A) + .invokestatic(DOUBLE, "valueOf", "(D)Ljava/lang/Double;").__() + }, + { // new Double(s) = Double.valueof(s) (ignoring identity) + ____.new_(DOUBLE) + .dup() + .getstatic(FIELD_D) + .invokespecial(DOUBLE, "", "(D)V").__(), + + ____.getstatic(FIELD_D) + .invokestatic(DOUBLE, "valueOf", "(D)Ljava/lang/Double;").__() + }, + { // new Double(v.f) = Double.valueof(v.f) (ignoring identity) + ____.new_(DOUBLE) + .dup() + .aload(A) + .getfield(FIELD_D) + .invokespecial(DOUBLE, "", "(D)V").__(), + + ____.aload(A) + .getfield(FIELD_D) + .invokestatic(DOUBLE, "valueOf", "(D)Ljava/lang/Double;").__() + }, + { // Double.valueOf(...).doubleValue() = nothing + ____.invokestatic(DOUBLE, "valueOf", "(D)Ljava/lang/Double;") + .invokevirtual(DOUBLE_VALUE).__(), + }, + }; + + STRING_SEQUENCES = new Instruction[][][] + { + { // "...".equals("...") = true + ____.ldc_(A) + .ldc_(A) + .invokevirtual(STRING, "equals", "(Ljava/lang/Object;)Z").__(), + + ____.iconst_1().__() + }, + { // "...".length() = ... + ____.ldc_(A) + .invokevirtual(STRING, "length", "()I").__(), + + ____.sipush(STRING_A_LENGTH).__() + }, + { // String.valueOf(Z) = ".... + ____.iconst(A) + .invokestatic(STRING, "valueOf", "(Z)Ljava/lang/String;").__(), + + ____.ldc_(BOOLEAN_A_STRING).__() + }, + { // String.valueOf(C) = "...." + ____.iconst(A) + .invokestatic(STRING, "valueOf", "(C)Ljava/lang/String;").__(), + + ____.ldc_(CHAR_A_STRING).__() + }, + { // String.valueOf(Cc) = "...." + ____.ldc_(A) + .invokestatic(STRING, "valueOf", "(C)Ljava/lang/String;").__(), + + ____.ldc_(CHAR_A_STRING).__() + }, + { // String.valueOf(I) = "...." + ____.iconst(A) + .invokestatic(STRING, "valueOf", "(I)Ljava/lang/String;").__(), + + ____.ldc_(INT_A_STRING).__() + }, + { // String.valueOf(Ic) = "...." + ____.ldc_(A) + .invokestatic(STRING, "valueOf", "(I)Ljava/lang/String;").__(), + + ____.ldc_(INT_A_STRING).__() + }, + { // String.valueOf(J) = "...." + ____.lconst(A) + .invokestatic(STRING, "valueOf", "(J)Ljava/lang/String;").__(), + + ____.ldc_(LONG_A_STRING).__() + }, + { // String.valueOf(Jc) = "...." + ____.ldc2_w(A) + .invokestatic(STRING, "valueOf", "(J)Ljava/lang/String;").__(), + + ____.ldc_(LONG_A_STRING).__() + }, + { // String.valueOf(F) = "...." + ____.fconst(A) + .invokestatic(STRING, "valueOf", "(F)Ljava/lang/String;").__(), + + ____.ldc_(FLOAT_A_STRING).__() + }, + { // String.valueOf(Fc) = "...." + ____.ldc_(A) + .invokestatic(STRING, "valueOf", "(F)Ljava/lang/String;").__(), + + ____.ldc_(FLOAT_A_STRING).__() + }, + { // String.valueOf(D) = "...." + ____.dconst(A) + .invokestatic(STRING, "valueOf", "(D)Ljava/lang/String;").__(), + + ____.ldc_(DOUBLE_A_STRING).__() + }, + { // String.valueOf(Dc) = "...." + ____.ldc2_w(A) + .invokestatic(STRING, "valueOf", "(D)Ljava/lang/String;").__(), + + ____.ldc_(DOUBLE_A_STRING).__() + }, + { // "...".concat("...") = "......" + ____.ldc_(A) + .ldc_(B) + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__(), + + ____.ldc_(STRING_A_STRING | STRING_B_STRING).__(), + }, + + { // new StringBuffer("...").toString() = "..." (ignoring identity) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A).__() + }, + { // new StringBuffer(string).toString() = string (ignoring identity and discarding any NullPointerException) + ____.new_(STRING_BUFFER) + .dup() + .aload(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .invokevirtual(TO_STRING).__(), + + ____.aload(A).__() + }, + { // new StringBuffer("...").length() = length + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .invokevirtual(STRING_BUFFER, "length", "()I").__(), + + ____.sipush(STRING_A_LENGTH).__() + }, + { // new StringBuffer() (without dup) = nothing + ____.new_(STRING_BUFFER) + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "()V").__(), + }, + { // new StringBuffer("...") (without dup) = nothing + ____.new_(STRING_BUFFER) + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__(), + }, + { // new StringBuffer()/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "()V") + .pop().__(), + }, + { // new StringBuffer("...")/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .pop().__(), + }, + { // new StringBuffer("...").append(z)/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUFFER, "append", "(Z)Ljava/lang/StringBuffer;") + .pop().__(), + }, + { // new StringBuffer("...").append(c)/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;") + .pop().__(), + }, + { // new StringBuffer("...").append(i)/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;") + .pop().__(), + }, + { // new StringBuffer("...").append(l)/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .lload(B) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;") + .pop().__(), + }, + { // new StringBuffer("...").append(f)/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .fload(B) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;") + .pop().__(), + }, + { // new StringBuffer("...").append(d)/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .dload(B) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;") + .pop().__(), + }, + { // new StringBuffer("...").append(s)/pop = nothing + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .aload(B) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .pop().__(), + }, + { // StringBuffer#toString()/pop = pop + ____.invokevirtual(TO_STRING) + .pop().__(), + + ____.pop().__() + }, + { // StringBuffer#append("") = nothing + ____.ldc("") + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__(), + }, + { // new StringBuffer().append(Z) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .iconst(A) + .invokevirtual(STRING_BUFFER, "append", "(Z)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(BOOLEAN_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(C) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .iconst(A) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(CHAR_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(Cc) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(CHAR_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(I) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .iconst(A) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(INT_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(Ic) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(INT_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(J) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .lconst(A) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(LONG_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(Jc) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .ldc2_w(A) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(LONG_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(F) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .fconst(A) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(FLOAT_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(Fc) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(FLOAT_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(D) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .dconst(A) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(DOUBLE_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append(Dc) = new StringBuffer("....") + ____.invokespecial(STRING_BUFFER, "", "()V") + .ldc2_w(A) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(DOUBLE_A_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer().append("...") = new StringBuffer("...") + ____.invokespecial(STRING_BUFFER, "", "()V") + .ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(Z) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iconst(B) + .invokevirtual(STRING_BUFFER, "append", "(Z)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | BOOLEAN_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(C) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iconst(B) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | CHAR_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(Cc) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .ldc_(B) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | CHAR_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(I) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iconst(B) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | INT_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(Ic) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .ldc_(B) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | INT_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(J) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .lconst(B) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | LONG_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(Jc) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .ldc2_w(B) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | LONG_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(F) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .fconst(B) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | FLOAT_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(Fc) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .ldc_(B) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | FLOAT_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(D) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .dconst(B) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | DOUBLE_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(Dc) = new StringBuffer("....") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .ldc2_w(B) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | DOUBLE_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append("...") = new StringBuffer("......") + ____.ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .ldc_(B) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | STRING_B_STRING) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuffer("...").append(z).toString() = "...".concat(String.valueOf(z)) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUFFER, "append", "(Z)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .iload(B) + .invokestatic(STRING, "valueOf", "(Z)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuffer("...").append(c).toString() = "...".concat(String.valueOf(c)) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .iload(B) + .invokestatic(STRING, "valueOf", "(C)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuffer("...").append(i).toString() = "...".concat(String.valueOf(i)) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .iload(B) + .invokestatic(STRING, "valueOf", "(I)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuffer("...").append(l).toString() = "...".concat(String.valueOf(l)) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .lload(B) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .lload(B) + .invokestatic(STRING, "valueOf", "(J)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuffer("...").append(f).toString() = "...".concat(String.valueOf(f)) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .fload(B) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .fload(B) + .invokestatic(STRING, "valueOf", "(F)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuffer("...").append(d).toString() = "...".concat(String.valueOf(d)) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .dload(B) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .dload(B) + .invokestatic(STRING, "valueOf", "(D)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuffer("...").append(string).toString() = "...".concat(String.valueOf(string)) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .aload(B) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .aload(B) + .invokestatic(STRING, "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuffer("...").append(object).toString() = "...".concat(String.valueOf(object)) + ____.new_(STRING_BUFFER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUFFER, "", "(Ljava/lang/String;)V") + .aload(B) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .aload(B) + .invokestatic(STRING, "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // StringBuffer#append("...").append(Z) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .iconst(B) + .invokevirtual(STRING_BUFFER, "append", "(Z)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | BOOLEAN_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(C) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .iconst(B) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | CHAR_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(Cc) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .ldc_(B) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | CHAR_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(I) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .iconst(B) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | INT_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(Ic) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .ldc_(B) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | INT_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(J) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .lconst(B) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | LONG_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(Jc) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .ldc2_w(B) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | LONG_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(F) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .fconst(B) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | FLOAT_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(Fc) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .ldc_(B) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | FLOAT_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(D) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .dconst(B) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | DOUBLE_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append(Dc) = StringBuffer#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .ldc2_w(B) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | DOUBLE_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // StringBuffer#append("...").append("...") = StringBuffer#append("......") + ____.ldc_(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .ldc_(B) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__(), + + ____.ldc_(STRING_A_STRING | STRING_B_STRING) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;").__() + }, + { // new StringBuffer().append(z).toString() = String.valueOf(z) + ____.new_(STRING_BUFFER) + .dup() + .invokespecial(STRING_BUFFER, "", "()V") + .iload(A) + .invokevirtual(STRING_BUFFER, "append", "(Z)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.iload(A) + .invokestatic(STRING, "valueOf", "(Z)Ljava/lang/String;").__() + }, + { // new StringBuffer().append(c).toString() = String.valueOf(c) + ____.new_(STRING_BUFFER) + .dup() + .invokespecial(STRING_BUFFER, "", "()V") + .iload(A) + .invokevirtual(STRING_BUFFER, "append", "(C)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.iload(A) + .invokestatic(STRING, "valueOf", "(C)Ljava/lang/String;").__() + }, + { // new StringBuffer().append(i).toString() = String.valueOf(i) + ____.new_(STRING_BUFFER) + .dup() + .invokespecial(STRING_BUFFER, "", "()V") + .iload(A) + .invokevirtual(STRING_BUFFER, "append", "(I)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.iload(A) + .invokestatic(STRING, "valueOf", "(I)Ljava/lang/String;").__() + }, + { // new StringBuffer().append(j).toString() = String.valueOf(j) + ____.new_(STRING_BUFFER) + .dup() + .invokespecial(STRING_BUFFER, "", "()V") + .lload(A) + .invokevirtual(STRING_BUFFER, "append", "(J)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.lload(A) + .invokestatic(STRING, "valueOf", "(J)Ljava/lang/String;").__() + }, + { // new StringBuffer().append(f).toString() = String.valueOf(f) + ____.new_(STRING_BUFFER) + .dup() + .invokespecial(STRING_BUFFER, "", "()V") + .fload(A) + .invokevirtual(STRING_BUFFER, "append", "(F)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.fload(A) + .invokestatic(STRING, "valueOf", "(F)Ljava/lang/String;").__() + }, + { // new StringBuffer().append(d).toString() = String.valueOf(d) + ____.new_(STRING_BUFFER) + .dup() + .invokespecial(STRING_BUFFER, "", "()V") + .dload(A) + .invokevirtual(STRING_BUFFER, "append", "(D)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.dload(A) + .invokestatic(STRING, "valueOf", "(D)Ljava/lang/String;").__() + }, + { // new StringBuffer().append(string).toString() = String.valueOf(string) + ____.new_(STRING_BUFFER) + .dup() + .invokespecial(STRING_BUFFER, "", "()V") + .aload(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.aload(A) + .invokestatic(STRING, "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;").__() + }, + { // new StringBuffer().append(object).toString() = String.valueOf(object) + ____.new_(STRING_BUFFER) + .dup() + .invokespecial(STRING_BUFFER, "", "()V") + .aload(A) + .invokevirtual(STRING_BUFFER, "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;") + .invokevirtual(TO_STRING).__(), + + ____.aload(A) + .invokestatic(STRING, "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;").__() + }, + + { // new StringBuilder("...").toString() = "..." (ignoring identity) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A).__() + }, + { // new StringBuilder(string).toString() = string (ignoring identity and discarding any NullPointerException) + ____.new_(STRING_BUILDER) + .dup() + .aload(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .invokevirtual(TO_STRING).__(), + + ____.aload(A).__() + }, + { // new StringBuilder("...").length() = length + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .invokevirtual(STRING_BUILDER, "length", "()I").__(), + + ____.sipush(STRING_A_LENGTH).__() + }, + { // new StringBuilder() (without dup) = nothing + ____.new_(STRING_BUILDER) + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "()V").__(), + }, + { // new StringBuilder("...") (without dup) = nothing + ____.new_(STRING_BUILDER) + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__(), + }, + { // new StringBuilder()/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "()V") + .pop().__(), + }, + { // new StringBuilder("...")/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .pop().__(), + }, + { // new StringBuilder("...").append(z)/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUILDER, "append", "(Z)Ljava/lang/StringBuilder;") + .pop().__(), + }, + { // new StringBuilder("...").append(c)/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;") + .pop().__(), + }, + { // new StringBuilder("...").append(i)/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;") + .pop().__(), + }, + { // new StringBuilder("...").append(l)/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .lload(B) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;") + .pop().__(), + }, + { // new StringBuilder("...").append(f)/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .fload(B) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;") + .pop().__(), + }, + { // new StringBuilder("...").append(d)/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .dload(B) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;") + .pop().__(), + }, + { // new StringBuilder("...").append(s)/pop = nothing + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .aload(B) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .pop().__(), + }, + { // StringBuilder#toString()/pop = pop + ____.invokevirtual(TO_STRING) + .pop().__(), + + ____.pop().__() + }, + { // StringBuilder#append("") = nothing + ____.ldc("") + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__(), + }, + { // new StringBuilder().append(Z) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .iconst(A) + .invokevirtual(STRING_BUILDER, "append", "(Z)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(BOOLEAN_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(C) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .iconst(A) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(CHAR_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(Cc) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(CHAR_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(I) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .iconst(A) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(INT_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(Ic) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(INT_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(J) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .lconst(A) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(LONG_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(Jc) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .ldc2_w(A) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(LONG_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(F) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .fconst(A) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(FLOAT_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(Fc) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(FLOAT_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(D) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .dconst(A) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(DOUBLE_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append(Dc) = new StringBuilder("....") + ____.invokespecial(STRING_BUILDER, "", "()V") + .ldc2_w(A) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(DOUBLE_A_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder().append("...") = new StringBuilder("...") + ____.invokespecial(STRING_BUILDER, "", "()V") + .ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(Z) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iconst(B) + .invokevirtual(STRING_BUILDER, "append", "(Z)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | BOOLEAN_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(C) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iconst(B) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | CHAR_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(Cc) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .ldc_(B) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | CHAR_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(I) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iconst(B) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | INT_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(Ic) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .ldc_(B) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | INT_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(J) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .lconst(B) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | LONG_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(Jc) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .ldc2_w(B) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | LONG_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(F) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .fconst(B) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | FLOAT_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(Fc) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .ldc_(B) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | FLOAT_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(D) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .dconst(B) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | DOUBLE_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(Dc) = new StringBuilder("....") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .ldc2_w(B) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | DOUBLE_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append("...") = new StringBuilder("......") + ____.ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .ldc_(B) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | STRING_B_STRING) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V").__() + }, + { // new StringBuilder("...").append(z).toString() = "...".concat(String.valueOf(z)) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUILDER, "append", "(Z)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .iload(B) + .invokestatic(STRING, "valueOf", "(Z)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuilder("...").append(c).toString() = "...".concat(String.valueOf(c)) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .iload(B) + .invokestatic(STRING, "valueOf", "(C)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuilder("...").append(i).toString() = "...".concat(String.valueOf(i)) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .iload(B) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .iload(B) + .invokestatic(STRING, "valueOf", "(I)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuilder("...").append(l).toString() = "...".concat(String.valueOf(l)) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .lload(B) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .lload(B) + .invokestatic(STRING, "valueOf", "(J)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuilder("...").append(f).toString() = "...".concat(String.valueOf(f)) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .fload(B) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .fload(B) + .invokestatic(STRING, "valueOf", "(F)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuilder("...").append(d).toString() = "...".concat(String.valueOf(d)) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .dload(B) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .dload(B) + .invokestatic(STRING, "valueOf", "(D)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuilder("...").append(string).toString() = "...".concat(String.valueOf(string)) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .aload(B) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .aload(B) + .invokestatic(STRING, "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // new StringBuilder("...").append(object).toString() = "...".concat(String.valueOf(object)) + ____.new_(STRING_BUILDER) + .dup() + .ldc_(A) + .invokespecial(STRING_BUILDER, "", "(Ljava/lang/String;)V") + .aload(B) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.ldc_(A) + .aload(B) + .invokestatic(STRING, "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;") + .invokevirtual(STRING, "concat", "(Ljava/lang/String;)Ljava/lang/String;").__() + }, + { // StringBuilder#append("...").append(Z) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .iconst(B) + .invokevirtual(STRING_BUILDER, "append", "(Z)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | BOOLEAN_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(C) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .iconst(B) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | CHAR_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(Cc) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .ldc_(B) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | CHAR_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(I) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .iconst(B) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | INT_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(Ic) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .ldc_(B) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | INT_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(J) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .lconst(B) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | LONG_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(Jc) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .ldc2_w(B) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | LONG_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(F) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .fconst(B) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | FLOAT_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(Fc) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .ldc_(B) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | FLOAT_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(D) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .dconst(B) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | DOUBLE_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append(Dc) = StringBuilder#append("....") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .ldc2_w(B) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | DOUBLE_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // StringBuilder#append("...").append("...") = StringBuilder#append("......") + ____.ldc_(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .ldc_(B) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__(), + + ____.ldc_(STRING_A_STRING | STRING_B_STRING) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;").__() + }, + { // new StringBuilder().append(z).toString() = String.valueOf(z) + ____.new_(STRING_BUILDER) + .dup() + .invokespecial(STRING_BUILDER, "", "()V") + .iload(A) + .invokevirtual(STRING_BUILDER, "append", "(Z)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.iload(A) + .invokestatic(STRING, "valueOf", "(Z)Ljava/lang/String;").__() + }, + { // new StringBuilder().append(c).toString() = String.valueOf(c) + ____.new_(STRING_BUILDER) + .dup() + .invokespecial(STRING_BUILDER, "", "()V") + .iload(A) + .invokevirtual(STRING_BUILDER, "append", "(C)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.iload(A) + .invokestatic(STRING, "valueOf", "(C)Ljava/lang/String;").__() + }, + { // new StringBuilder().append(i).toString() = String.valueOf(i) + ____.new_(STRING_BUILDER) + .dup() + .invokespecial(STRING_BUILDER, "", "()V") + .iload(A) + .invokevirtual(STRING_BUILDER, "append", "(I)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.iload(A) + .invokestatic(STRING, "valueOf", "(I)Ljava/lang/String;").__() + }, + { // new StringBuilder().append(j).toString() = String.valueOf(j) + ____.new_(STRING_BUILDER) + .dup() + .invokespecial(STRING_BUILDER, "", "()V") + .lload(A) + .invokevirtual(STRING_BUILDER, "append", "(J)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.lload(A) + .invokestatic(STRING, "valueOf", "(J)Ljava/lang/String;").__() + }, + { // new StringBuilder().append(f).toString() = String.valueOf(f) + ____.new_(STRING_BUILDER) + .dup() + .invokespecial(STRING_BUILDER, "", "()V") + .fload(A) + .invokevirtual(STRING_BUILDER, "append", "(F)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.fload(A) + .invokestatic(STRING, "valueOf", "(F)Ljava/lang/String;").__() + }, + { // new StringBuilder().append(d).toString() = String.valueOf(d) + ____.new_(STRING_BUILDER) + .dup() + .invokespecial(STRING_BUILDER, "", "()V") + .dload(A) + .invokevirtual(STRING_BUILDER, "append", "(D)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.dload(A) + .invokestatic(STRING, "valueOf", "(D)Ljava/lang/String;").__() + }, + { // new StringBuilder().append(string).toString() = String.valueOf(string) + ____.new_(STRING_BUILDER) + .dup() + .invokespecial(STRING_BUILDER, "", "()V") + .aload(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.aload(A) + .invokestatic(STRING, "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;").__() + }, + { // new StringBuilder().append(object).toString() = String.valueOf(object) + ____.new_(STRING_BUILDER) + .dup() + .invokespecial(STRING_BUILDER, "", "()V") + .aload(A) + .invokevirtual(STRING_BUILDER, "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;") + .invokevirtual(TO_STRING).__(), + + ____.aload(A) + .invokestatic(STRING, "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;").__() + }, + }; + + MATH_SEQUENCES = new Instruction[][][] + { + { // (float)Math.abs((double)...) = Math.abs(...) + ____.f2d() + .invokestatic(MATH, "abs", "(D)D") + .d2f().__(), + + ____.invokestatic(MATH, "abs", "(F)F").__() + }, + { // (float)Math.abs(...) = Math.abs((float)...) + ____.invokestatic(MATH, "abs", "(D)D") + .d2f().__(), + + ____.d2f() + .invokestatic(MATH, "abs", "(F)F").__() + }, + { // (int)Math.floor((double)...) = ... + ____.i2d() + .invokestatic(MATH, "floor", "(D)D") + .d2i().__(), + }, + { // (int)Math.ceil((double)...) = ... + ____.i2d() + .invokestatic(MATH, "ceil", "(D)D") + .d2i().__(), + }, + { // (float)Math.min((double)..., 0.0) = Math.min(..., 0f) + ____.f2d() + .dconst_0() + .invokestatic(MATH, "min", "(DD)D") + .d2f().__(), + + ____.fconst_0() + .invokestatic(MATH, "min", "(FF)F").__() + }, + { // (float)Math.min(..., 0.0) = Math.min((float)..., 0f) (assuming in float range) + ____.dconst_0() + .invokestatic(MATH, "min", "(DD)D") + .d2f().__(), + + ____.d2f() + .fconst_0() + .invokestatic(MATH, "min", "(FF)F").__() + }, + { // (float)Math.max((double)..., 0.0) = Math.max(..., 0f) + ____.f2d() + .dconst_0() + .invokestatic(MATH, "max", "(DD)D") + .d2f().__(), + + ____.fconst_0() + .invokestatic(MATH, "max", "(FF)F").__() + }, + { // (float)Math.max(..., 0.0) = Math.max((float)..., 0f) (assuming in float range) + ____.dconst_0() + .invokestatic(MATH, "max", "(DD)D") + .d2f().__(), + + ____.d2f() + .fconst_0() + .invokestatic(MATH, "max", "(FF)F").__() + }, + }; + + MATH_ANDROID_SEQUENCES = new Instruction[][][] + { + // As of API level 22, FloatMath has been deprecated, as the + // equivalent methods in Math are faster on Android versions + // with a JIT. We therefore now convert from FloatMath to Math. + + { // FloatMath.sqrt((float)...) = (float)Math.sqrt(...) + ____.d2f() + .invokestatic(FLOAT_MATH, "sqrt", "(F)F").__(), + + ____.invokestatic(MATH, "sqrt", "(D)D") + .d2f().__() + }, + { // FloatMath.sqrt(...) = (float)Math.sqrt((double)...) + ____.invokestatic(FLOAT_MATH, "sqrt", "(F)F").__(), + + ____.f2d() + .invokestatic(MATH, "sqrt", "(D)D") + .d2f().__() + }, + { // FloatMath.cos((float)...) = (float)Math.cos(...) + ____.d2f() + .invokestatic(FLOAT_MATH, "cos", "(F)F").__(), + + ____.invokestatic(MATH, "cos", "(D)D") + .d2f().__() + }, + { // FloatMath.cos(...) = (float)Math.cos((double)...) + ____.invokestatic(FLOAT_MATH, "cos", "(F)F").__(), + + ____.f2d() + .invokestatic(MATH, "cos", "(D)D") + .d2f().__() + }, + { // FloatMath.sin((float)...) = (float)Math.sin(...) + ____.d2f() + .invokestatic(FLOAT_MATH, "sin", "(F)F").__(), + + ____.invokestatic(MATH, "sin", "(D)D") + .d2f().__() + }, + { // FloatMath.sin(...) = (float)Math.sin((double)...) + ____.invokestatic(FLOAT_MATH, "sin", "(F)F").__(), + + ____.f2d() + .invokestatic(MATH, "sin", "(D)D") + .d2f().__() + }, + { // FloatMath.floor((float)...) = (float)Math.floor(...) + ____.d2f() + .invokestatic(FLOAT_MATH, "floor", "(F)F").__(), + + ____.invokestatic(MATH, "floor", "(D)D") + .d2f().__() + }, + { // FloatMath.floor(...) = (float)Math.floor((double)...) + ____.invokestatic(FLOAT_MATH, "floor", "(F)F").__(), + + ____.f2d() + .invokestatic(MATH, "floor", "(D)D") + .d2f().__() + }, + { // FloatMath.ceil((float)...) = (float)Math.ceil(...) + ____.d2f() + .invokestatic(FLOAT_MATH, "ceil", "(F)F").__(), + + ____.invokestatic(MATH, "ceil", "(D)D") + .d2f().__() + }, + { // FloatMath.ceil(...) = (float)Math.ceil((double)...) + ____.invokestatic(FLOAT_MATH, "ceil", "(F)F").__(), + + ____.f2d() + .invokestatic(MATH, "ceil", "(D)D") + .d2f().__() + }, + }; + + CONSTANTS = ____.constants(); + } + + + /** + * Prints out the instruction sequences. + */ + public static void main(String[] args) + { + InstructionSequenceConstants instructionSequenceConstants = + new InstructionSequenceConstants(new ClassPool(), + new ClassPool()); + + Instruction[][][][] sets = new Instruction[][][][] + { + instructionSequenceConstants.VARIABLE_SEQUENCES, + instructionSequenceConstants.ARITHMETIC_SEQUENCES, + instructionSequenceConstants.FIELD_SEQUENCES, + instructionSequenceConstants.CAST_SEQUENCES, + instructionSequenceConstants.BRANCH_SEQUENCES, + instructionSequenceConstants.STRING_SEQUENCES, + instructionSequenceConstants.OBJECT_SEQUENCES, + instructionSequenceConstants.MATH_SEQUENCES, + instructionSequenceConstants.MATH_ANDROID_SEQUENCES, + }; + + ProgramClass clazz = new ProgramClass(); + clazz.constantPool = instructionSequenceConstants.CONSTANTS; + + for (int setIndex = 0; setIndex < sets.length; setIndex++) + { + Instruction[][][] sequencePairs = sets[setIndex]; + + for (int sequencePairIndex = 0; sequencePairIndex < sequencePairs.length; sequencePairIndex++) + { + Instruction[][] sequencePair = sequencePairs[sequencePairIndex]; + + // Print out the pattern instructions. + Instruction[] sequence = sequencePair[0]; + for (int index = 0; index < sequence.length; index++) + { + Instruction instruction = sequence[index]; + try + { + instruction.accept(clazz, null, null, index, new ClassPrinter()); + } + catch (Exception e) {} + } + + // Are there any replacement instructions? + if (sequencePair.length < 2) + { + System.out.println("=> delete"); + } + else + { + System.out.println("=>"); + + // Print out the replacement instructions. + sequence = sequencePair[1]; + for (int index = 0; index < sequence.length; index++) + { + Instruction instruction = sequence[index]; + try + { + instruction.accept(clazz, null, null, index, new ClassPrinter()); + } + catch (Exception e) {} + } + } + System.out.println(); + } + } + } +} diff --git a/core/src/proguard/optimize/peephole/InstructionSequenceReplacer.java b/core/src/proguard/optimize/peephole/InstructionSequenceReplacer.java new file mode 100644 index 000000000..16cd0ae80 --- /dev/null +++ b/core/src/proguard/optimize/peephole/InstructionSequenceReplacer.java @@ -0,0 +1,875 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.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. + * + * The class also supports labels ({@link #label()}) and exception handlers + * ({@link #catch_(int,int,int)}) in replacement sequences. They provide + * local branch offsets inside the replacement sequences + * ({@link Label#offset()}). For example, creating a replacement sequence + * with the help of {@link InstructionSequenceBuilder}: + * + * final InstructionSequenceReplacer.Label TRY_START = InstructionSequenceReplacer.label(); + * final InstructionSequenceReplacer.Label TRY_END = InstructionSequenceReplacer.label(); + * final InstructionSequenceReplacer.Label CATCH_END = InstructionSequenceReplacer.label(); + * + * final InstructionSequenceReplacer.Label CATCH_EXCEPTION = + * InstructionSequenceReplacer.catch_(TRY_START.offset(), + * TRY_END.offset(), + * constantPoolEditor.addClassConstant("java/lang/Exception", null)); + * + * Instructions[] replacementInstructions = builder + * .label(TRY_START) + * ...... + * .label(TRY_END) + * .goto_(CATCH_END.offset()) + * .catch_(CATCH_EXCEPTION) + * ...... + * .athrow() + * .label(CATCH_END) + * ...... + * .instructions(); + * + * + * @see InstructionSequenceMatcher + * @author Eric Lafortune + */ +public class InstructionSequenceReplacer +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + public static boolean DEBUG = System.getProperty("isr") != null; + //*/ + + 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; + public static final int E = InstructionSequenceMatcher.E; + public static final int F = InstructionSequenceMatcher.F; + public static final int G = InstructionSequenceMatcher.G; + public static final int H = InstructionSequenceMatcher.H; + public static final int I = InstructionSequenceMatcher.I; + public static final int J = InstructionSequenceMatcher.J; + public static final int K = InstructionSequenceMatcher.K; + public static final int L = InstructionSequenceMatcher.L; + public static final int M = InstructionSequenceMatcher.M; + public static final int N = InstructionSequenceMatcher.N; + public static final int O = InstructionSequenceMatcher.O; + public static final int P = InstructionSequenceMatcher.P; + public static final int Q = InstructionSequenceMatcher.Q; + public static final int R = InstructionSequenceMatcher.R; + + private static final int LABEL_FLAG = 0x20000000; + + 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; + + // Replacement constants that are derived from matched variables. + 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 static int labelCounter; + + + 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 + * instructions. + * @param patternInstructions the pattern instruction sequence. + * @param replacementConstants any constants referenced by the + * replacement instructions. + * @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, + Constant[] replacementConstants, + Instruction[] replacementInstructions, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor) + { + this(patternConstants, + patternInstructions, + replacementConstants, + replacementInstructions, + branchTargetFinder, + codeAttributeEditor, + null); + } + + + /** + * Creates a new InstructionSequenceReplacer. + * @param patternConstants any constants referenced by the pattern + * instructions. + * @param patternInstructions the pattern instruction sequence. + * @param replacementConstants any constants referenced by the + * replacement instructions. + * @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. + * @param extraInstructionVisitor an optional extra visitor for all deleted + * load instructions. + */ + public InstructionSequenceReplacer(Constant[] patternConstants, + Instruction[] patternInstructions, + Constant[] replacementConstants, + Instruction[] replacementInstructions, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor, + InstructionVisitor extraInstructionVisitor) + { + this(new InstructionSequenceMatcher(patternConstants, patternInstructions), + patternConstants, + patternInstructions, + replacementConstants, + replacementInstructions, + branchTargetFinder, + codeAttributeEditor, + extraInstructionVisitor); + } + + + + /** + * Creates a new InstructionSequenceReplacer. + * @param instructionSequenceMatcher a suitable instruction sequence matcher. + * @param patternConstants any constants referenced by the pattern + * instructions. + * @param patternInstructions the pattern instruction sequence. + * @param replacementConstants any constants referenced by the + * replacement instructions. + * @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. + * @param extraInstructionVisitor an optional extra visitor for all deleted + * load instructions. + */ + protected InstructionSequenceReplacer(InstructionSequenceMatcher instructionSequenceMatcher, + Constant[] patternConstants, + Instruction[] patternInstructions, + Constant[] replacementConstants, + Instruction[] replacementInstructions, + BranchTargetFinder branchTargetFinder, + CodeAttributeEditor codeAttributeEditor, + InstructionVisitor extraInstructionVisitor) + { + this.instructionSequenceMatcher = instructionSequenceMatcher; + 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 != null && + 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()) + { + int patternCount = instructionSequenceMatcher.instructionCount(); + int replacementCount = replacementInstructions.length; + + if (DEBUG) + { + System.out.println("InstructionSequenceReplacer: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + System.out.println(" Matched:"); + for (int index = 0; index < patternCount; 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 < replacementCount; index++) + { + int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(Math.min(index, patternCount-1)); + System.out.println(" " + replacementInstructionFactory.create(clazz, codeAttribute, index).shrink().toString(matchedOffset)); + } + } + + // Is the replacement sequence shorter than the pattern sequence? + if (replacementCount <= patternCount) + { + // Replace the instruction sequence. + for (int index = 0; index < replacementCount; index++) + { + codeAttributeEditor.replaceInstruction(instructionSequenceMatcher.matchedInstructionOffset(index), + replacementInstructionFactory.create(clazz, codeAttribute, index)); + } + + // Delete any remaining instructions in the matched sequence. + for (int index = replacementCount; index < patternCount; index++) + { + codeAttributeEditor.deleteInstruction(instructionSequenceMatcher.matchedInstructionOffset(index)); + } + } + else + { + // Replace the instruction sequence. + for (int index = 0; index < patternCount; index++) + { + codeAttributeEditor.replaceInstruction(instructionSequenceMatcher.matchedInstructionOffset(index), + replacementInstructionFactory.create(clazz, codeAttribute, index)); + } + + // Append the remaining instructions in the replacement + // sequence. + Instruction[] extraInstructions = new Instruction[replacementCount - patternCount]; + for (int index = 0; index < extraInstructions.length; index++) + { + extraInstructions[index] = replacementInstructionFactory.create(clazz, codeAttribute, patternCount + index); + } + + codeAttributeEditor.insertAfterInstruction(instructionSequenceMatcher.matchedInstructionOffset(patternCount - 1), + extraInstructions); + } + + // 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, CodeAttribute codeAttribute, int index) + { + int matchedInstructionIndex = + index < instructionSequenceMatcher.instructionCount() ? + index : + instructionSequenceMatcher.instructionCount() - 1; + + int matchedInstructionOffset = + instructionSequenceMatcher.matchedInstructionOffset(matchedInstructionIndex); + + // Create the instruction. + replacementInstructions[index].accept(clazz, + null, + codeAttribute, + matchedInstructionOffset, + 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, method, codeAttribute, offset, simpleInstruction.constant)); + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + replacementInstruction = + new VariableInstruction(variableInstruction.opcode, + matchedArgument(clazz, method, codeAttribute, offset, 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, + matchedBranchOffset(offset, branchInstruction.branchOffset)); + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + replacementInstruction = + new TableSwitchInstruction(tableSwitchInstruction.opcode, + matchedBranchOffset(offset, tableSwitchInstruction.defaultOffset), + instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.lowCase), + instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.highCase), + matchedJumpOffsets(offset, tableSwitchInstruction.jumpOffsets)); + + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + replacementInstruction = + new LookUpSwitchInstruction(lookUpSwitchInstruction.opcode, + matchedBranchOffset(offset, lookUpSwitchInstruction.defaultOffset), + instructionSequenceMatcher.matchedArguments(lookUpSwitchInstruction.cases), + matchedJumpOffsets(offset, lookUpSwitchInstruction.jumpOffsets)); + } + + + // Similar methods for pseudo-instructions. + + public void visitLabelInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Label label) + { + // Convert this pseudo-instruction into a corresponding + // pseudo-instruction for the code attribute editor. + // Then make sure we create a unique label, because + // there may be other matching sequences. + replacementInstruction = + codeAttributeEditor.label(uniqueLabel(label.identifier)); + } + + + public void visitCatchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Catch catch_) + { + // Convert this pseudo-instruction into a corresponding + // pseudo-instruction for the code attribute editor. + // Then make sure we create and reference unique labels, + // because there may be other matching sequences. + replacementInstruction = + codeAttributeEditor.catch_(uniqueLabel(catch_.identifier), + uniqueLabel(catch_.startOfffset), + uniqueLabel(catch_.endOffset), + matchedConstantIndex((ProgramClass)clazz, + catch_.catchType)); + } + } + + + /** + * Returns the matched argument for the given pattern argument. + */ + protected int matchedArgument(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int argument) + { + return matchedArgument(clazz, argument); + } + + + /** + * Returns the matched argument for the given pattern argument. + */ + protected 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. + */ + protected 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); + } + + + /** + * Returns the value of the specified matched branch offset. + */ + protected int matchedBranchOffset(int offset, int branchOffset) + { + // Special case: is it a label? + if (isLabel(branchOffset)) + { + // Then make sure we reference a unique label, because + // there may be other matching sequences. + return uniqueLabel(branchOffset); + } + + // Otherwise, just return the matched branch offset. + return instructionSequenceMatcher.matchedBranchOffset(offset, branchOffset); + } + + + /** + * Returns the values of the specified matched jump offsets. + */ + protected int[] matchedJumpOffsets(int offset, int[] jumpOffsets) + { + // Special cases: are there any labels? + for (int index = 0; index < jumpOffsets.length; index++) + { + if (isLabel(jumpOffsets[index])) + { + // Then make sure we reference a unique label, because + // there may be other matching sequences. + jumpOffsets[index] = uniqueLabel(jumpOffsets[index]); + } + } + + // Match any other jump offsets. + return instructionSequenceMatcher.matchedJumpOffsets(offset, jumpOffsets); + } + + + private String argumentAsString(ProgramClass programClass, + int valueType, + int argument) + { + switch (valueType) + { + case BOOLEAN_STRING: + return Boolean.toString((wasConstant(argument) ? + ((IntegerConstant)matchedConstant(programClass, argument)).getValue() : + matchedArgument(argument)) != 0); + + case CHAR_STRING: + return Character.toString((char)(wasConstant(argument) ? + ((IntegerConstant)(matchedConstant(programClass, argument))).getValue() : + matchedArgument(argument))); + + case INT_STRING: + return Integer.toString(wasConstant(argument) ? + ((IntegerConstant)(matchedConstant(programClass, argument))).getValue() : + matchedArgument(argument)); + + case LONG_STRING: + return Long.toString(wasConstant(argument) ? + ((LongConstant)(matchedConstant(programClass, argument))).getValue() : + matchedArgument(argument)); + + case FLOAT_STRING: + return Float.toString(wasConstant(argument) ? + ((FloatConstant)(matchedConstant(programClass, argument))).getValue() : + matchedArgument(argument)); + + case DOUBLE_STRING: + return Double.toString(wasConstant(argument) ? + ((DoubleConstant)(matchedConstant(programClass, argument))).getValue() : + matchedArgument(argument)); + + case STRING_STRING: + return + programClass.getStringString(instructionSequenceMatcher.matchedConstantIndex(argument)); + + default: + return ""; + } + } + + + protected InstructionSequenceMatcher getInstructionSequenceMatcher() + { + return instructionSequenceMatcher; + } + + + protected boolean wasConstant(int argument) + { + return instructionSequenceMatcher.wasConstant(argument); + } + + + protected Constant matchedConstant(ProgramClass programClass, + int argument) + { + return programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)); + } + + + protected int matchedArgument(int argument) + { + return instructionSequenceMatcher.matchedArgument(argument); + } + + + /** + * Makes the given label identifier or offset unique for the current + * matching instruction sequence. + */ + private int uniqueLabel(int labelIdentifier) + { + return labelIdentifier | + (instructionSequenceMatcher.matchedInstructionOffset(0) << 8); + } + + + // For convenience, we also define two pseudo-instructions, to conveniently + // mark local labels and create new exceptions handlers. + + /** + * Creates a new label that can be used as a pseudo-instruction to mark + * a local offset. Its offset can be used as a branch target in + * replacement instructions ({@link Label#offset()}). + */ + public static Label label() + { + return new Label(labelCounter++); + } + + + /** + * Creates a new catch instance that can be used as a pseudo-instruction + * to mark the start of an exception handler. Its offset can be used as + * a branch target in replacement instructions ({@link Label#offset()}). + */ + public static Label catch_(int startOffset, + int endOffset, + int catchType) + { + return new Catch(labelCounter++, + startOffset, + endOffset, + catchType); + } + + + /** + * Returns whether the given instruction offset actually represents a + * label (which contains the actual offset). + */ + private static boolean isLabel(int instructionOffset) + { + return (instructionOffset & 0xff000000) == LABEL_FLAG; + } + + + /** + * This pseudo-instruction represents a label that marks an instruction + * offset, for use in the context of the sequence replacer only. + */ + public static class Label + extends Instruction + { + protected final int identifier; + + + /** + * Creates a new Label. + * @param identifier an identifier that can be chosen freely. + */ + private Label(int identifier) + { + this.identifier = identifier; + } + + + /** + * Returns the offset that can then be used as a branch target in + * other replacement instructions. + */ + public int offset() + { + return LABEL_FLAG | identifier; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + return this; + } + + + public void write(byte[] code, int offset) + { + } + + + protected void readInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't read label instruction"); + } + + + protected void writeInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't write label instruction"); + } + + + public int length(int offset) + { + return 0; + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + // Since this is not a standard instruction, it only works with + // our own instruction visitor. + MyReplacementInstructionFactory replacementInstructionFactory = + (MyReplacementInstructionFactory)instructionVisitor; + + replacementInstructionFactory.visitLabelInstruction(clazz, + method, + codeAttribute, + offset, + this); + } + + + // Implementations for Object. + + public String toString() + { + return "label_"+offset(); + } + } + + + /** + * This pseudo-instruction represents an exception handler, + * for use in the context of the sequence replacer only. + */ + private static class Catch + extends Label + { + private final int startOfffset; + private final int endOffset; + private final int catchType; + + + /** + * Creates a new Catch instance. + * @param identifier an identifier that can be chosen freely. + * @param startOffset the start offset of the catch block. + * @param endOffset the end offset of the catch block. + * @param catchType the index of the catch type in the constant pool. + */ + private Catch(int identifier, + int startOffset, + int endOffset, + int catchType) + { + super(identifier); + + this.startOfffset = startOffset; + this.endOffset = endOffset; + this.catchType = catchType; + } + + + // Implementations for Instruction. + + public Instruction shrink() + { + return this; + } + + + public void write(byte[] code, int offset) + { + } + + + protected void readInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't read catch instruction"); + } + + + protected void writeInfo(byte[] code, int offset) + { + throw new UnsupportedOperationException("Can't write catch instruction"); + } + + + public int length(int offset) + { + return super.length(offset); + } + + + public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) + { + // Since this is not a standard instruction, it only works with + // our own instruction visitor. + MyReplacementInstructionFactory replacementInstructionFactory = + (MyReplacementInstructionFactory)instructionVisitor; + + replacementInstructionFactory.visitCatchInstruction(clazz, + method, + codeAttribute, + offset, + this); + } + + + // Implementations for Object. + + public String toString() + { + return "catch " + + (isLabel(startOfffset) ? "label_" : "") + startOfffset + ", " + + (isLabel(endOffset) ? "label_" : "") + endOffset + ", #" + + catchType; + } + } +} diff --git a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java b/core/src/proguard/optimize/peephole/InstructionSequencesReplacer.java similarity index 77% rename from src/proguard/optimize/peephole/InstructionSequencesReplacer.java rename to core/src/proguard/optimize/peephole/InstructionSequencesReplacer.java index 3b52654e0..faa27fd5d 100644 --- a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java +++ b/core/src/proguard/optimize/peephole/InstructionSequencesReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -24,11 +24,13 @@ import proguard.classfile.editor.CodeAttributeEditor; import proguard.classfile.instruction.Instruction; import proguard.classfile.instruction.visitor.*; -import proguard.evaluation.BranchTargetFinder; /** * This InstructionVisitor replaces multiple instruction sequences at once. * + * The replacement sequences are optional, defaulting to the empty sequence, + * to delete the matched pattern sequences. + * * @see InstructionSequenceReplacer * @author Eric Lafortune */ @@ -39,11 +41,13 @@ public class InstructionSequencesReplacer private static final int PATTERN_INDEX = 0; private static final int REPLACEMENT_INDEX = 1; + private static final Instruction[] EMPTY_INSTRUCTIONS = new Instruction[0]; + /** * Creates a new InstructionSequencesReplacer. - * @param patternConstants any constants referenced by the pattern - * instruction. + * @param constants any constants referenced by the pattern + * instructions and replacement instructions. * @param instructionSequences the instruction sequences to be replaced, * with subsequently the sequence pair index, * the patten/replacement index (0 or 1), @@ -54,12 +58,12 @@ public class InstructionSequencesReplacer * @param codeAttributeEditor a code editor that can be used for * accumulating changes to the code. */ - public InstructionSequencesReplacer(Constant[] patternConstants, + public InstructionSequencesReplacer(Constant[] constants, Instruction[][][] instructionSequences, BranchTargetFinder branchTargetFinder, CodeAttributeEditor codeAttributeEditor) { - this(patternConstants, + this(constants, instructionSequences, branchTargetFinder, codeAttributeEditor, @@ -69,8 +73,8 @@ public InstructionSequencesReplacer(Constant[] patternConstants, /** * Creates a new InstructionSequenceReplacer. - * @param patternConstants any constants referenced by the pattern - * instruction. + * @param constants any constants referenced by the pattern + * instructions and replacement instructions. * @param instructionSequences the instruction sequences to be replaced, * with subsequently the sequence pair index, * the patten/replacement index (0 or 1), @@ -83,13 +87,13 @@ public InstructionSequencesReplacer(Constant[] patternConstants, * @param extraInstructionVisitor an optional extra visitor for all deleted * load instructions. */ - public InstructionSequencesReplacer(Constant[] patternConstants, + public InstructionSequencesReplacer(Constant[] constants, Instruction[][][] instructionSequences, BranchTargetFinder branchTargetFinder, CodeAttributeEditor codeAttributeEditor, InstructionVisitor extraInstructionVisitor) { - super(createInstructionSequenceReplacers(patternConstants, + super(createInstructionSequenceReplacers(constants, instructionSequences, branchTargetFinder, codeAttributeEditor, @@ -99,8 +103,8 @@ public InstructionSequencesReplacer(Constant[] patternConstants, /** * Creates an array of InstructionSequenceReplacer instances. - * @param patternConstants any constants referenced by the pattern - * instruction. + * @param constants any constants referenced by the pattern + * instructions and replacement instructions. * @param instructionSequences the instruction sequences to be replaced, * with subsequently the sequence pair index, * the from/to index (0 or 1), and the @@ -113,22 +117,34 @@ public InstructionSequencesReplacer(Constant[] patternConstants, * @param extraInstructionVisitor an optional extra visitor for all deleted * load instructions. */ - private static InstructionVisitor[] createInstructionSequenceReplacers(Constant[] patternConstants, + private static InstructionVisitor[] createInstructionSequenceReplacers(Constant[] constants, Instruction[][][] instructionSequences, BranchTargetFinder branchTargetFinder, CodeAttributeEditor codeAttributeEditor, InstructionVisitor extraInstructionVisitor) { InstructionVisitor[] instructionSequenceReplacers = - new InstructionSequenceReplacer[instructionSequences.length]; + new InstructionVisitor[instructionSequences.length]; for (int index = 0; index < instructionSequenceReplacers.length; index++) { - Instruction[][] instructionSequencePair = instructionSequences[index]; + Instruction[][] instructionSequencePair = + instructionSequences[index]; + + Instruction[] patternInstructions = + instructionSequencePair[PATTERN_INDEX]; + + // The replacement sequence is optional. + Instruction[] replacementInstructions = + instructionSequencePair.length > REPLACEMENT_INDEX ? + instructionSequencePair[REPLACEMENT_INDEX] : + EMPTY_INSTRUCTIONS; + instructionSequenceReplacers[index] = - new InstructionSequenceReplacer(patternConstants, - instructionSequencePair[PATTERN_INDEX], - instructionSequencePair[REPLACEMENT_INDEX], + new InstructionSequenceReplacer(constants, + patternInstructions, + constants, + replacementInstructions, branchTargetFinder, codeAttributeEditor, extraInstructionVisitor); diff --git a/src/proguard/optimize/peephole/LineNumberLinearizer.java b/core/src/proguard/optimize/peephole/LineNumberLinearizer.java similarity index 99% rename from src/proguard/optimize/peephole/LineNumberLinearizer.java rename to core/src/proguard/optimize/peephole/LineNumberLinearizer.java index f7bba0363..a87726159 100644 --- a/src/proguard/optimize/peephole/LineNumberLinearizer.java +++ b/core/src/proguard/optimize/peephole/LineNumberLinearizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/MemberPrivatizer.java b/core/src/proguard/optimize/peephole/MemberPrivatizer.java similarity index 98% rename from src/proguard/optimize/peephole/MemberPrivatizer.java rename to core/src/proguard/optimize/peephole/MemberPrivatizer.java index 223ab5921..027fa0734 100644 --- a/src/proguard/optimize/peephole/MemberPrivatizer.java +++ b/core/src/proguard/optimize/peephole/MemberPrivatizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/MethodFinalizer.java b/core/src/proguard/optimize/peephole/MethodFinalizer.java similarity index 98% rename from src/proguard/optimize/peephole/MethodFinalizer.java rename to core/src/proguard/optimize/peephole/MethodFinalizer.java index 9ad158520..7ba5c874a 100644 --- a/src/proguard/optimize/peephole/MethodFinalizer.java +++ b/core/src/proguard/optimize/peephole/MethodFinalizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/MethodInliner.java b/core/src/proguard/optimize/peephole/MethodInliner.java similarity index 84% rename from src/proguard/optimize/peephole/MethodInliner.java rename to core/src/proguard/optimize/peephole/MethodInliner.java index 13d1e9963..67ec01e98 100644 --- a/src/proguard/optimize/peephole/MethodInliner.java +++ b/core/src/proguard/optimize/peephole/MethodInliner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,13 +27,13 @@ import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.editor.*; import proguard.classfile.instruction.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.instruction.visitor.*; import proguard.classfile.util.*; import proguard.classfile.visitor.*; -import proguard.optimize.KeepMarker; +import proguard.optimize.*; import proguard.optimize.info.*; -import java.util.*; +import java.util.Stack; /** * This AttributeVisitor inlines short methods or methods that are only invoked @@ -47,31 +47,41 @@ public class MethodInliner InstructionVisitor, ConstantVisitor, MemberVisitor, + ExceptionInfoVisitor, LineNumberInfoVisitor { + private static final int MAXIMUM_INLINED_CODE_LENGTH_JVM = Integer.parseInt(System.getProperty("maximum.inlined.code.length", "8")); + private static final int MAXIMUM_INLINED_CODE_LENGTH_android= Integer.parseInt(System.getProperty("maximum.inlined.code.length", "32")); + 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")); + static final int METHOD_DUMMY_START_LINE_NUMBER = 0; static final int INLINED_METHOD_END_LINE_NUMBER = -1; - 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; + public static boolean DEBUG = System.getProperty("mi") != null; //*/ private final boolean microEdition; + private final boolean android; 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 final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); + private final MemberVisitor accessMethodMarker = new OptimizationInfoMemberFilter( + new AllAttributeVisitor( + new AllInstructionVisitor( + new MultiInstructionVisitor( + new SuperInvocationMarker(), + new AccessMethodMarker() + )))); + private final AttributeVisitor methodInvocationMarker = new AllInstructionVisitor( + new MethodInvocationMarker()); + private final StackSizeComputer stackSizeComputer = new StackSizeComputer(); private ProgramClass targetClass; private ProgramMethod targetMethod; @@ -81,6 +91,8 @@ public class MethodInliner private boolean inlining; private Stack inliningMethods = new Stack(); private boolean emptyInvokingStack; + private boolean coveredByCatchAllHandler; + private int exceptionInfoCount; private int uninitializedObjectCount; private int variableOffset; private boolean inlined; @@ -94,6 +106,8 @@ public class MethodInliner * Creates a new MethodInliner. * @param microEdition indicates whether the resulting code is * targeted at Java Micro Edition. + * @param android indicates whether the resulting code is + * targeted at the androidVM. * @param allowAccessModification indicates whether the access modifiers of * classes and class members can be changed * in order to inline methods. @@ -102,10 +116,12 @@ public class MethodInliner * short methods. */ public MethodInliner(boolean microEdition, + boolean android, boolean allowAccessModification, boolean inlineSingleInvocations) { this(microEdition, + android, allowAccessModification, inlineSingleInvocations, null); @@ -116,6 +132,8 @@ public MethodInliner(boolean microEdition, * Creates a new MethodInliner. * @param microEdition indicates whether the resulting code is * targeted at Java Micro Edition. + * @param android indicates whether the resulting code is + * targeted at the androidVM. * @param allowAccessModification indicates whether the access modifiers of * classes and class members can be changed * in order to inline methods. @@ -126,11 +144,13 @@ public MethodInliner(boolean microEdition, * inlined invocation instructions. */ public MethodInliner(boolean microEdition, + boolean android, boolean allowAccessModification, boolean inlineSingleInvocations, InstructionVisitor extraInlinedInvocationVisitor) { this.microEdition = microEdition; + this.android = android; this.allowAccessModification = allowAccessModification; this.inlineSingleInvocations = inlineSingleInvocations; this.extraInlinedInvocationVisitor = extraInlinedInvocationVisitor; @@ -202,27 +222,27 @@ public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAt // 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); + // Update the super/private/package/protected accessing flags. + method.accept(clazz, accessMethodMarker); } + + targetClass = null; + targetMethod = null; + constantAdder = null; } // 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) && + codeAttribute.u4codeLength <= + (android? + MAXIMUM_INLINED_CODE_LENGTH_android: + MAXIMUM_INLINED_CODE_LENGTH_JVM)) && estimatedResultingCodeLength + codeAttribute.u4codeLength < (microEdition ? MAXIMUM_RESULTING_CODE_LENGTH_JME : @@ -508,6 +528,13 @@ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute c variableOffset += codeAttribute.u2maxLocals; + // Check if the method invocation is covered by a catch-all + // exception handler. + coveredByCatchAllHandler = false; + exceptionInfoCount = 0; + codeAttribute.exceptionsAccept(clazz, method, offset, this); + coveredByCatchAllHandler = exceptionInfoCount > 0 ? coveredByCatchAllHandler : true; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); variableOffset -= codeAttribute.u2maxLocals; @@ -568,8 +595,8 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM ClassConstants.ACC_FINAL)) != 0 && // Only inline the method if it is not synchronized, etc. - (accessFlags & (ClassConstants.ACC_SYNCHRONIZED | - ClassConstants.ACC_NATIVE | + (accessFlags & (ClassConstants.ACC_SYNCHRONIZED | + ClassConstants.ACC_NATIVE | ClassConstants.ACC_ABSTRACT)) == 0 && // Don't inline an method, except in an method in the @@ -577,7 +604,7 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM // (!programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT) || // (programClass.equals(targetClass) && // targetMethod.getName(targetClass).equals(ClassConstants.METHOD_NAME_INIT))) && - !programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT) && + !programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT) && // Don't inline a method into itself. (!programMethod.equals(targetMethod) || @@ -617,6 +644,13 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM (!AccessMethodMarker.accessesProtectedCode(programMethod) || programClass.equals(targetClass)) && + // if the method to be inlined has a synchronized block only inline it into + // the target method if its invocation is covered by a catchall handler or + // none at all. This might happen if the target method has been obfuscated + // with fake exception handlers. + (!SynchronizedBlockMethodMarker.hasSynchronizedBlock(programMethod) || + coveredByCatchAllHandler) && + // Only inline the method if it doesn't catch exceptions, or if it // is invoked with an empty stack. (!CatchExceptionMarker.catchesExceptions(programMethod) || @@ -626,12 +660,14 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM // stack. !NonEmptyStackReturnMarker.returnsWithNonEmptyStack(programMethod) && - // a subset of the initialized superclasses. - ((accessFlags & ClassConstants.ACC_STATIC) == 0 || - programClass.equals(targetClass) || - initializedSuperClasses(targetClass).containsAll(initializedSuperClasses(programClass)))) + // Only inline the method if its related static initializers don't + // have any side effects. + !SideEffectClassChecker.mayHaveSideEffects(targetClass, + programClass, + programMethod)) { boolean oldInlining = inlining; + inlining = true; inliningMethods.push(programMethod); @@ -639,13 +675,16 @@ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programM programMethod.attributesAccept(programClass, this); // Update the optimization information of the target method. - MethodOptimizationInfo info = - MethodOptimizationInfo.getMethodOptimizationInfo(targetMethod); - if (info != null) + if (!KeepMarker.isKept(targetMethod)) { - info.merge(MethodOptimizationInfo.getMethodOptimizationInfo(programMethod)); + ProgramMethodOptimizationInfo.getProgramMethodOptimizationInfo(targetMethod) + .merge(MethodOptimizationInfo.getMethodOptimizationInfo(programMethod)); } + // Increment the invocation count of referenced methods again, + // since they are now invoked from the inlined code too. + programMethod.attributesAccept(programClass, methodInvocationMarker); + inlining = oldInlining; inliningMethods.pop(); } @@ -694,19 +733,11 @@ public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAt } - /** - * Returns the set of superclasses and interfaces that are initialized. - */ - private Set initializedSuperClasses(Clazz clazz) - { - Set set = new HashSet(); + // Implementations for ExceptionInfoVisitor. - // 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; + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + exceptionInfoCount++; + coveredByCatchAllHandler |= exceptionInfo.u2catchType == 0; } } diff --git a/src/proguard/optimize/peephole/NopRemover.java b/core/src/proguard/optimize/peephole/NopRemover.java similarity index 98% rename from src/proguard/optimize/peephole/NopRemover.java rename to core/src/proguard/optimize/peephole/NopRemover.java index fb40bea62..1fd4ac2e5 100644 --- a/src/proguard/optimize/peephole/NopRemover.java +++ b/core/src/proguard/optimize/peephole/NopRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/PeepholeOptimizer.java b/core/src/proguard/optimize/peephole/PeepholeOptimizer.java similarity index 96% rename from src/proguard/optimize/peephole/PeepholeOptimizer.java rename to core/src/proguard/optimize/peephole/PeepholeOptimizer.java index 10d2f80ea..3d0b3c42d 100644 --- a/src/proguard/optimize/peephole/PeepholeOptimizer.java +++ b/core/src/proguard/optimize/peephole/PeepholeOptimizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,7 +26,6 @@ import proguard.classfile.editor.CodeAttributeEditor; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; -import proguard.evaluation.BranchTargetFinder; /** * This AttributeVisitor sets up and applies the peephole optimizations of its @@ -39,7 +38,7 @@ public class PeepholeOptimizer extends SimplifiedVisitor implements AttributeVisitor { - private final BranchTargetFinder branchTargetFinder; + private final BranchTargetFinder branchTargetFinder; private final CodeAttributeEditor codeAttributeEditor; private final InstructionVisitor instructionVisitor; diff --git a/src/proguard/optimize/peephole/ReachableCodeMarker.java b/core/src/proguard/optimize/peephole/ReachableCodeMarker.java similarity index 99% rename from src/proguard/optimize/peephole/ReachableCodeMarker.java rename to core/src/proguard/optimize/peephole/ReachableCodeMarker.java index 0e9a31959..30c328048 100644 --- a/src/proguard/optimize/peephole/ReachableCodeMarker.java +++ b/core/src/proguard/optimize/peephole/ReachableCodeMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/RetargetedClassFilter.java b/core/src/proguard/optimize/peephole/RetargetedClassFilter.java similarity index 98% rename from src/proguard/optimize/peephole/RetargetedClassFilter.java rename to core/src/proguard/optimize/peephole/RetargetedClassFilter.java index 60e7eac50..248ab2c2c 100644 --- a/src/proguard/optimize/peephole/RetargetedClassFilter.java +++ b/core/src/proguard/optimize/peephole/RetargetedClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java b/core/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java similarity index 99% rename from src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java rename to core/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java index 34364b488..fe6f10116 100644 --- a/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java +++ b/core/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/TargetClassChanger.java b/core/src/proguard/optimize/peephole/TargetClassChanger.java similarity index 99% rename from src/proguard/optimize/peephole/TargetClassChanger.java rename to core/src/proguard/optimize/peephole/TargetClassChanger.java index 6b5352581..d95d8141e 100644 --- a/src/proguard/optimize/peephole/TargetClassChanger.java +++ b/core/src/proguard/optimize/peephole/TargetClassChanger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/UnreachableCodeRemover.java b/core/src/proguard/optimize/peephole/UnreachableCodeRemover.java similarity index 98% rename from src/proguard/optimize/peephole/UnreachableCodeRemover.java rename to core/src/proguard/optimize/peephole/UnreachableCodeRemover.java index aa07d1188..44ea3786a 100644 --- a/src/proguard/optimize/peephole/UnreachableCodeRemover.java +++ b/core/src/proguard/optimize/peephole/UnreachableCodeRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/UnreachableExceptionRemover.java b/core/src/proguard/optimize/peephole/UnreachableExceptionRemover.java similarity index 98% rename from src/proguard/optimize/peephole/UnreachableExceptionRemover.java rename to core/src/proguard/optimize/peephole/UnreachableExceptionRemover.java index 8dad4468b..795843985 100644 --- a/src/proguard/optimize/peephole/UnreachableExceptionRemover.java +++ b/core/src/proguard/optimize/peephole/UnreachableExceptionRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/VariableShrinker.java b/core/src/proguard/optimize/peephole/VariableShrinker.java similarity index 98% rename from src/proguard/optimize/peephole/VariableShrinker.java rename to core/src/proguard/optimize/peephole/VariableShrinker.java index 8f01954dd..afe0de248 100644 --- a/src/proguard/optimize/peephole/VariableShrinker.java +++ b/core/src/proguard/optimize/peephole/VariableShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/VerticalClassMerger.java b/core/src/proguard/optimize/peephole/VerticalClassMerger.java similarity index 88% rename from src/proguard/optimize/peephole/VerticalClassMerger.java rename to core/src/proguard/optimize/peephole/VerticalClassMerger.java index 2f7fc00bb..4ad1bd4f2 100644 --- a/src/proguard/optimize/peephole/VerticalClassMerger.java +++ b/core/src/proguard/optimize/peephole/VerticalClassMerger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,7 +22,7 @@ import proguard.classfile.ProgramClass; import proguard.classfile.util.SimplifiedVisitor; -import proguard.classfile.visitor.ClassVisitor; +import proguard.classfile.visitor.*; /** * This ClassVisitor inlines the direct subclasses into the program classes @@ -35,13 +35,14 @@ public class VerticalClassMerger extends SimplifiedVisitor implements ClassVisitor { - private final boolean allowAccessModification; - private final boolean mergeInterfacesAggressively; - private final ClassVisitor extraClassVisitor; + 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. @@ -57,6 +58,7 @@ public VerticalClassMerger(boolean allowAccessModification, /** * Creates a new VerticalClassMerger. + * @param allowAccessModification specifies whether the access modifiers * of classes can be changed in order to * merge them. @@ -67,7 +69,7 @@ public VerticalClassMerger(boolean allowAccessModification, */ public VerticalClassMerger(boolean allowAccessModification, boolean mergeInterfacesAggressively, - ClassVisitor extraClassVisitor) + ClassVisitor extraClassVisitor ) { this.allowAccessModification = allowAccessModification; this.mergeInterfacesAggressively = mergeInterfacesAggressively; @@ -83,6 +85,7 @@ public void visitProgramClass(ProgramClass programClass) programClass.subclassesAccept(new ClassMerger(programClass, allowAccessModification, mergeInterfacesAggressively, + false, extraClassVisitor)); } } \ No newline at end of file diff --git a/core/src/proguard/optimize/peephole/WildcardConstantFilter.java b/core/src/proguard/optimize/peephole/WildcardConstantFilter.java new file mode 100644 index 000000000..f96198385 --- /dev/null +++ b/core/src/proguard/optimize/peephole/WildcardConstantFilter.java @@ -0,0 +1,300 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.Clazz; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; + +/** + * This ConstantVisitor delegates to a given constant visitor, except for + * constants that contain wildcards (indices larger than 0xffff). + * + * @see InstructionSequenceMatcher + * @author Eric Lafortune + */ +public class WildcardConstantFilter +implements ConstantVisitor +{ + private static final int WILDCARD = InstructionSequenceMatcher.X; + + + private final ConstantVisitor constantVisitor; + + private final MyWildcardChecker wildcardChecker = new MyWildcardChecker(); + + + /** + * Creates a new WildcardClassReferenceInitializer that delegates to the + * given constant visitor. + */ + public WildcardConstantFilter(ConstantVisitor constantVisitor) + { + this.constantVisitor = constantVisitor; + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + constantVisitor.visitIntegerConstant(clazz, integerConstant); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + constantVisitor.visitLongConstant(clazz, longConstant); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + constantVisitor.visitFloatConstant(clazz, floatConstant); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + constantVisitor.visitDoubleConstant(clazz, doubleConstant); + } + + + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + constantVisitor.visitPrimitiveArrayConstant(clazz, primitiveArrayConstant); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + if (!containsWildcard(clazz, stringConstant)) + { + constantVisitor.visitStringConstant(clazz, stringConstant); + } + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + constantVisitor.visitUtf8Constant(clazz, utf8Constant); + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + if (!containsWildcard(clazz, invokeDynamicConstant)) + { + constantVisitor.visitInvokeDynamicConstant(clazz, invokeDynamicConstant); + } + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + constantVisitor.visitMethodHandleConstant(clazz, methodHandleConstant); + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + if (!containsWildcard(clazz, fieldrefConstant)) + { + constantVisitor.visitFieldrefConstant(clazz, fieldrefConstant); + } + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + if (!containsWildcard(clazz, interfaceMethodrefConstant)) + { + constantVisitor.visitInterfaceMethodrefConstant(clazz, interfaceMethodrefConstant); + } + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + if (!containsWildcard(clazz, methodrefConstant)) + { + constantVisitor.visitMethodrefConstant(clazz, methodrefConstant); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + if (!containsWildcard(clazz, classConstant)) + { + constantVisitor.visitClassConstant(clazz, classConstant); + } + } + + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + if (!containsWildcard(clazz, methodTypeConstant)) + { + constantVisitor.visitMethodTypeConstant(clazz, methodTypeConstant); + } + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + if (!containsWildcard(clazz, nameAndTypeConstant)) + { + constantVisitor.visitNameAndTypeConstant(clazz, nameAndTypeConstant); + } + } + + + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + if (!containsWildcard(clazz, moduleConstant)) + { + constantVisitor.visitModuleConstant(clazz, moduleConstant); + } + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + if (!containsWildcard(clazz, packageConstant)) + { + constantVisitor.visitPackageConstant(clazz, packageConstant); + } + } + + + // Small utility methods. + + /** + * Checks whether the given constant contains wildcard indices. + */ + private boolean containsWildcard(Clazz clazz, Constant constant) + { + wildcardChecker.containsWildcard = false; + constant.accept(clazz, wildcardChecker); + return wildcardChecker.containsWildcard; + } + + + + private static class MyWildcardChecker + extends SimplifiedVisitor + implements ConstantVisitor + { + public boolean containsWildcard; + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + if (stringConstant.u2stringIndex >= WILDCARD) + { + containsWildcard = true; + } + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + if (invokeDynamicConstant.u2nameAndTypeIndex >= WILDCARD) + { + containsWildcard = true; + } + else + { + // Recursively check the referenced constant. + clazz.constantPoolEntryAccept(invokeDynamicConstant.u2nameAndTypeIndex, this); + } + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + if (refConstant.u2classIndex >= WILDCARD || + refConstant.u2nameAndTypeIndex >= WILDCARD) + { + containsWildcard = true; + } + else + { + // Recursively check the referenced constants. + clazz.constantPoolEntryAccept(refConstant.u2classIndex, this); + clazz.constantPoolEntryAccept(refConstant.u2nameAndTypeIndex, this); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + if (classConstant.u2nameIndex >= WILDCARD) + { + containsWildcard = true; + } + } + + + public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) + { + if (methodTypeConstant.u2descriptorIndex >= WILDCARD) + { + containsWildcard = true; + } + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + if (nameAndTypeConstant.u2nameIndex >= WILDCARD || + nameAndTypeConstant.u2descriptorIndex >= WILDCARD) + { + containsWildcard = true; + } + } + + + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + if (moduleConstant.u2nameIndex >= WILDCARD) + { + containsWildcard = true; + } + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + if (packageConstant.u2nameIndex >= WILDCARD) + { + containsWildcard = true; + } + } + } +} diff --git a/core/src/proguard/optimize/peephole/WrapperClassMerger.java b/core/src/proguard/optimize/peephole/WrapperClassMerger.java new file mode 100644 index 000000000..83cd9af8d --- /dev/null +++ b/core/src/proguard/optimize/peephole/WrapperClassMerger.java @@ -0,0 +1,99 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.ClassConstants; +import proguard.classfile.Clazz; +import proguard.classfile.ProgramClass; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; +import proguard.optimize.info.WrapperClassMarker; + +/** + * This ClassVisitor inlines the wrapper classes that it visits into their + * wrapped classes, whenever possible. + * + * A wrapper class can be a simple (anonymous) inner class that implements + * some interface, for example. + * + * @see WrapperClassMarker + * @see ClassMerger + * @author Eric Lafortune + */ +public class WrapperClassMerger +extends SimplifiedVisitor +implements ClassVisitor +{ + private final boolean allowAccessModification; + private final ClassVisitor extraClassVisitor; + + + /** + * Creates a new WrappedClassMerger. + * + * @param allowAccessModification specifies whether the access modifiers + * of classes can be changed in order to + * merge them. + */ + public WrapperClassMerger(boolean allowAccessModification) + { + this(allowAccessModification, null); + } + + + /** + * Creates a new WrappedClassMerger. + + * @param allowAccessModification specifies whether the access modifiers + * of classes can be changed in order to + * merge them. + * @param extraClassVisitor an optional extra visitor for all + * merged classes. + */ + public WrapperClassMerger(boolean allowAccessModification, + ClassVisitor extraClassVisitor) + { + this.allowAccessModification = allowAccessModification; + this.extraClassVisitor = extraClassVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Only merge wrapper classes that extend java.lang.Object. + if (ClassConstants.NAME_JAVA_LANG_OBJECT.equals(programClass.getSuperName())) + { + // Do we have a wrapped program class? + Clazz wrappedClass = WrapperClassMarker.getWrappedClass(programClass); + if (wrappedClass instanceof ProgramClass) + { + // Try inlining the wrapper class into the wrapped class. + new ClassMerger((ProgramClass)wrappedClass, + allowAccessModification, + false, + true, + extraClassVisitor).visitProgramClass(programClass); + } + } + } +} \ No newline at end of file diff --git a/core/src/proguard/optimize/peephole/WrapperClassUseSimplifier.java b/core/src/proguard/optimize/peephole/WrapperClassUseSimplifier.java new file mode 100644 index 000000000..d2f482148 --- /dev/null +++ b/core/src/proguard/optimize/peephole/WrapperClassUseSimplifier.java @@ -0,0 +1,275 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.Attribute; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.FieldrefConstant; +import proguard.classfile.constant.MethodrefConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.CodeAttributeEditor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; +import proguard.optimize.info.WrapperClassMarker; + +/** + * This AttributeVisitor simplifies the use of retargeted wrapper classes in + * the code attributes that it visits. More specifically, it replaces + * "new Wrapper(targetClass)" by "targetClass", and + * "wrapper.field" by "wrapper". + * You still need to retarget all class references, replacing references to + * the wrapper class by references to the target class. + * + * @see WrapperClassMarker + * @see ClassMerger + * @see RetargetedClassFilter + * @see TargetClassChanger + * @author Eric Lafortune + */ +public class WrapperClassUseSimplifier +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + ClassVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("wc") != null; + //*/ + + + private final InstructionVisitor extraInstructionVisitor; + + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true); + + // Fields acting as parameters and return values for the visitor methods. + private Clazz wrappedClass; + private Instruction popInstruction; + + + /** + * Creates a new WrapperClassUseSimplifier. + */ + public WrapperClassUseSimplifier() + { + this(null); + } + + + /** + * Creates a new WrapperClassUseSimplifier. + * @param extraInstructionVisitor an optional extra visitor for all + * simplified instructions. + */ + public WrapperClassUseSimplifier(InstructionVisitor extraInstructionVisitor) + { + this.extraInstructionVisitor = extraInstructionVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println("WrapperClassUseSimplifier: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + } + + int codeLength = codeAttribute.u4codeLength; + + // Reset the code changes. + codeAttributeEditor.reset(codeLength); + + // Edit the instructions. + 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) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_NEW: + { + // Is it instantiating a wrapper class? + if (isReferencingWrapperClass(clazz, constantInstruction.constantIndex)) + { + // Is the next instruction a typical dup instruction? + int nextOffset = offset + constantInstruction.length(offset); + popInstruction = InstructionFactory.create(codeAttribute.code, nextOffset); + switch (popInstruction.canonicalOpcode()) + { + case InstructionConstants.OP_DUP: + { + // Delete the new and dup instructions: + // new Wrapper + // dup + codeAttributeEditor.deleteInstruction(offset); + codeAttributeEditor.deleteInstruction(nextOffset); + popInstruction = null; + break; + } + case InstructionConstants.OP_ASTORE: + { + // Replace the new instance by a dummy null + // and remember to store the target instance: + // new Wrapper -> aconst_null + // astore x -> remember + // aload x + codeAttributeEditor.replaceInstruction(offset, new SimpleInstruction(InstructionConstants.OP_ACONST_NULL)); + break; + } + default: + { + // Replace the new instance by a dummy null + // and remember to pop the target instance: + // new Wrapper -> aconst_null + codeAttributeEditor.replaceInstruction(offset, new SimpleInstruction(InstructionConstants.OP_ACONST_NULL)); + popInstruction = new SimpleInstruction(InstructionConstants.OP_POP); + } + } + + if (extraInstructionVisitor != null) + { + extraInstructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } + } + break; + } + case InstructionConstants.OP_INVOKESPECIAL: + { + // Is it initializing a wrapper class? + if (isReferencingWrapperClass(clazz, constantInstruction.constantIndex)) + { + // Do we have a special pop instruction? + // TODO: May still fail with nested initializers. + if (popInstruction == null) + { + // Delete the initializer invocation (with the + // wrapper instance no longer on the stack): + // Wrapper.(target) -> target + codeAttributeEditor.deleteInstruction(offset); + } + else + { + // Delete the initializer invocation, and store + // the target instance again: + // invokespecial Wrapper.(target) -> astore x / pop + codeAttributeEditor.replaceInstruction(offset, new Instruction[] + { + popInstruction, + new SimpleInstruction(InstructionConstants.OP_POP), + }); + } + } + break; + } + case InstructionConstants.OP_GETFIELD: + { + // Is it retrieving the field of the wrapper class? + if (isReferencingWrapperClass(clazz, constantInstruction.constantIndex)) + { + // Delete the retrieval: + // wrapper.field -> wrapper. + codeAttributeEditor.deleteInstruction(offset); + } + break; + } + } + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + // Is the constant retrieving from a wrapper class? + fieldrefConstant.referencedClassAccept(this); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + if (methodrefConstant.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT)) + { + // Is the constant referring to a wrapper class? + methodrefConstant.referencedClassAccept(this); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Is the constant referring to a wrapper class? + classConstant.referencedClassAccept(this); + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + + public void visitProgramClass(ProgramClass programClass) + { + wrappedClass = ClassMerger.getTargetClass(programClass); + } + + + // Small utility methods. + + /** + * Returns whether the constant at the given offset is referencing a + * wrapper class (different from the given class) that is being retargeted. + */ + private boolean isReferencingWrapperClass(Clazz clazz, int constantIndex) + { + wrappedClass = null; + + clazz.constantPoolEntryAccept(constantIndex, this); + + return wrappedClass != null; + } +} diff --git a/src/proguard/optimize/peephole/package.html b/core/src/proguard/optimize/peephole/package.html similarity index 100% rename from src/proguard/optimize/peephole/package.html rename to core/src/proguard/optimize/peephole/package.html diff --git a/src/proguard/package.html b/core/src/proguard/package.html similarity index 100% rename from src/proguard/package.html rename to core/src/proguard/package.html diff --git a/src/proguard/preverify/CodePreverifier.java b/core/src/proguard/preverify/CodePreverifier.java similarity index 86% rename from src/proguard/preverify/CodePreverifier.java rename to core/src/proguard/preverify/CodePreverifier.java index 93c77ccb7..04c3f8748 100644 --- a/src/proguard/preverify/CodePreverifier.java +++ b/core/src/proguard/preverify/CodePreverifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -47,15 +47,22 @@ public class CodePreverifier //* private static final boolean DEBUG = false; /*/ - private static boolean DEBUG = true; + private static boolean DEBUG = System.getProperty("cp") != null; //*/ + private static final int AT_METHOD_ENTRY = -1; + private final boolean microEdition; - private final PartialEvaluator partialEvaluator = new PartialEvaluator(); - private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer(partialEvaluator); - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + private final ReferenceTracingValueFactory referenceTracingValueFactory = new ReferenceTracingValueFactory(new TypedReferenceValueFactory()); + private final PartialEvaluator partialEvaluator = new PartialEvaluator(referenceTracingValueFactory, + new ReferenceTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory)), + true, + referenceTracingValueFactory); + private final InitializationFinder initializationFinder = new InitializationFinder(partialEvaluator, false); + private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer(partialEvaluator, false, initializationFinder, false); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); /** @@ -105,7 +112,8 @@ public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAt int codeLength = codeAttribute.u4codeLength; // Evaluate the method. - //partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + initializationFinder.visitCodeAttribute(clazz, method, codeAttribute); livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute); // We may have to remove unreachable code. @@ -154,7 +162,7 @@ else if (partialEvaluator.isBranchOrExceptionTarget(offset)) correspondingVerificationTypes(programClass, programMethod, codeAttribute, - PartialEvaluator.AT_METHOD_ENTRY, + AT_METHOD_ENTRY, partialEvaluator.getVariablesBefore(0)); // Special case: the method. @@ -277,7 +285,7 @@ private VerificationType[] correspondingVerificationTypes(ProgramClass progra // Remember the maximum live type index. if (value != null && - (offset == PartialEvaluator.AT_METHOD_ENTRY || + (offset == AT_METHOD_ENTRY || livenessAnalyzer.isAliveBefore(offset, index))) { typeCount = typeIndex; @@ -306,14 +314,13 @@ private VerificationType[] correspondingVerificationTypes(ProgramClass progra VerificationType type; if (value != null && - (offset == PartialEvaluator.AT_METHOD_ENTRY || + (offset == AT_METHOD_ENTRY || livenessAnalyzer.isAliveBefore(offset, index))) { type = correspondingVerificationType(programClass, programMethod, codeAttribute, offset, - index == 0, value, producerValue); @@ -381,7 +388,6 @@ private VerificationType[] correspondingVerificationTypes(ProgramClass programC programMethod, codeAttribute, offset, - false, value, producerValue); @@ -405,7 +411,6 @@ private VerificationType correspondingVerificationType(ProgramClass programClas ProgramMethod programMethod, CodeAttribute codeAttribute, int offset, - boolean isVariable0, Value value, Value producerValue) { @@ -424,6 +429,7 @@ private VerificationType correspondingVerificationType(ProgramClass programClas 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(); @@ -433,40 +439,39 @@ private VerificationType correspondingVerificationType(ProgramClass programClas } // Does the reference type have a single producer? - if (offset != PartialEvaluator.AT_METHOD_ENTRY) + if (offset != AT_METHOD_ENTRY) { - InstructionOffsetValue producers = producerValue.instructionOffsetValue(); - if (producers.instructionOffsetCount() == 1) - { - int producerOffset = producers.instructionOffset(0); + TracedReferenceValue tracedReferenceValue = + (TracedReferenceValue)referenceValue; - // 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(); - } + InstructionOffsetValue instructionOffsetValue = + tracedReferenceValue.getTraceValue().instructionOffsetValue(); - // 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)) + if (instructionOffsetValue.instructionOffsetCount() == 1) + { + if (instructionOffsetValue.isMethodParameter(0)) { - // It's an UninitializedThis type. - return VerificationTypeFactory.createUninitializedThisType(); + // Are we in an instance initialization method, + // before the super initialization, loading "this"? + if (instructionOffsetValue.methodParameter(0) == 0 && + initializationFinder.isInitializer() && + offset <= initializationFinder.superInitializationOffset()) + { + // 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)) + else if (instructionOffsetValue.isNewinstance(0)) { - // It's an Uninitialized type. - return VerificationTypeFactory.createUninitializedType(producerOffset); + int producerOffset = instructionOffsetValue.instructionOffset(0); + + // Is the reference type newly created and still + // uninitialized? + if (!initializationFinder.isInitializedBefore(offset, instructionOffsetValue)) + { + // It's an Uninitialized type. + return VerificationTypeFactory.createUninitializedType(producerOffset); + } } } } @@ -576,7 +581,7 @@ else if (//previousVariablesCount > 0 && // Remember this frame. previousVariablesCount = fullFrame.variablesCount; - previousVariableTypes = fullFrame.variables; + previousVariableTypes = fullFrame.variables; // Replace the full frame. stackMapFrameList.set(index, compressedFrame); diff --git a/src/proguard/preverify/CodeSubroutineInliner.java b/core/src/proguard/preverify/CodeSubroutineInliner.java similarity index 99% rename from src/proguard/preverify/CodeSubroutineInliner.java rename to core/src/proguard/preverify/CodeSubroutineInliner.java index df6627d70..2851a1c37 100644 --- a/src/proguard/preverify/CodeSubroutineInliner.java +++ b/core/src/proguard/preverify/CodeSubroutineInliner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,7 +28,7 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.*; -import proguard.evaluation.BranchTargetFinder; +import proguard.optimize.peephole.BranchTargetFinder; /** * This AttributeVisitor inlines local subroutines (jsr/ret) in the code diff --git a/src/proguard/preverify/Preverifier.java b/core/src/proguard/preverify/Preverifier.java similarity index 97% rename from src/proguard/preverify/Preverifier.java rename to core/src/proguard/preverify/Preverifier.java index 2f5f51d56..3f80ec64c 100644 --- a/src/proguard/preverify/Preverifier.java +++ b/core/src/proguard/preverify/Preverifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/preverify/SubroutineInliner.java b/core/src/proguard/preverify/SubroutineInliner.java similarity index 94% rename from src/proguard/preverify/SubroutineInliner.java rename to core/src/proguard/preverify/SubroutineInliner.java index 90701ee3d..248372442 100644 --- a/src/proguard/preverify/SubroutineInliner.java +++ b/core/src/proguard/preverify/SubroutineInliner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -61,7 +61,8 @@ public void execute(ClassPool programClassPool) // In Java Standard Edition, only class files from Java 6 or higher // should be preverified. - if (!configuration.microEdition) + if (!configuration.microEdition && + !configuration.android) { inliner = new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_6, diff --git a/src/proguard/shrink/AnnotationUsageMarker.java b/core/src/proguard/shrink/AnnotationUsageMarker.java similarity index 99% rename from src/proguard/shrink/AnnotationUsageMarker.java rename to core/src/proguard/shrink/AnnotationUsageMarker.java index cd0f5fae1..ab73aa029 100644 --- a/src/proguard/shrink/AnnotationUsageMarker.java +++ b/core/src/proguard/shrink/AnnotationUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/ClassShrinker.java b/core/src/proguard/shrink/ClassShrinker.java similarity index 96% rename from src/proguard/shrink/ClassShrinker.java rename to core/src/proguard/shrink/ClassShrinker.java index 7e7dfe10a..9f016dc92 100644 --- a/src/proguard/shrink/ClassShrinker.java +++ b/core/src/proguard/shrink/ClassShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -30,14 +30,12 @@ import proguard.classfile.util.*; import proguard.classfile.visitor.*; -import java.util.Arrays; +import java.util.*; /** * 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 @@ -86,13 +84,23 @@ public void visitProgramClass(ProgramClass programClass) shrinkConstantPool(programClass.constantPool, programClass.u2constantPoolCount); + int oldFieldsCount = programClass.u2fieldsCount; programClass.u2fieldsCount = shrinkArray(programClass.fields, programClass.u2fieldsCount); + if (programClass.u2fieldsCount < oldFieldsCount) + { + programClass.u2accessFlags |= ClassConstants.ACC_REMOVED_FIELDS; + } + int oldMethodsCount = programClass.u2methodsCount; programClass.u2methodsCount = shrinkArray(programClass.methods, programClass.u2methodsCount); + if (programClass.u2methodsCount < oldMethodsCount) + { + programClass.u2accessFlags |= ClassConstants.ACC_REMOVED_METHODS; + } programClass.u2attributesCount = shrinkArray(programClass.attributes, @@ -166,8 +174,8 @@ public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribut { bootstrapMethodsAttribute.u2bootstrapMethodsCount = newBootstrapMethodsCount; - // Remap all constant pool references. - bootstrapMethodRemapper.setConstantIndexMap(bootstrapMethodIndexMap); + // Remap all bootstrap method references. + bootstrapMethodRemapper.setBootstrapMethodIndexMap(bootstrapMethodIndexMap); clazz.constantPoolEntriesAccept(bootstrapMethodRemapper); } } @@ -422,7 +430,7 @@ private boolean[] shrinkFlags(Constant[] constantPool, int[] array, int length) { boolean[] unused = new boolean[length]; - // Shift the used objects together. + // Remember the unused constants. for (int index = 0; index < length; index++) { if (!usageMarker.isUsed(constantPool[array[index]])) diff --git a/src/proguard/shrink/InnerUsageMarker.java b/core/src/proguard/shrink/InnerUsageMarker.java similarity index 98% rename from src/proguard/shrink/InnerUsageMarker.java rename to core/src/proguard/shrink/InnerUsageMarker.java index d0dd18635..e1a3ccc35 100644 --- a/src/proguard/shrink/InnerUsageMarker.java +++ b/core/src/proguard/shrink/InnerUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/InterfaceUsageMarker.java b/core/src/proguard/shrink/InterfaceUsageMarker.java similarity index 98% rename from src/proguard/shrink/InterfaceUsageMarker.java rename to core/src/proguard/shrink/InterfaceUsageMarker.java index 327d719dd..fed358434 100644 --- a/src/proguard/shrink/InterfaceUsageMarker.java +++ b/core/src/proguard/shrink/InterfaceUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/LocalVariableTypeUsageMarker.java b/core/src/proguard/shrink/LocalVariableTypeUsageMarker.java similarity index 99% rename from src/proguard/shrink/LocalVariableTypeUsageMarker.java rename to core/src/proguard/shrink/LocalVariableTypeUsageMarker.java index 9c63455b1..b21ef760a 100644 --- a/src/proguard/shrink/LocalVariableTypeUsageMarker.java +++ b/core/src/proguard/shrink/LocalVariableTypeUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/ShortestUsageMark.java b/core/src/proguard/shrink/ShortestUsageMark.java similarity index 98% rename from src/proguard/shrink/ShortestUsageMark.java rename to core/src/proguard/shrink/ShortestUsageMark.java index bdfe99156..4ffaedd4b 100644 --- a/src/proguard/shrink/ShortestUsageMark.java +++ b/core/src/proguard/shrink/ShortestUsageMark.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/ShortestUsageMarker.java b/core/src/proguard/shrink/ShortestUsageMarker.java similarity index 99% rename from src/proguard/shrink/ShortestUsageMarker.java rename to core/src/proguard/shrink/ShortestUsageMarker.java index d3da17cde..73d5d378d 100644 --- a/src/proguard/shrink/ShortestUsageMarker.java +++ b/core/src/proguard/shrink/ShortestUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/ShortestUsagePrinter.java b/core/src/proguard/shrink/ShortestUsagePrinter.java similarity index 99% rename from src/proguard/shrink/ShortestUsagePrinter.java rename to core/src/proguard/shrink/ShortestUsagePrinter.java index cd7da48c8..eb6897acd 100644 --- a/src/proguard/shrink/ShortestUsagePrinter.java +++ b/core/src/proguard/shrink/ShortestUsagePrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/Shrinker.java b/core/src/proguard/shrink/Shrinker.java similarity index 87% rename from src/proguard/shrink/Shrinker.java rename to core/src/proguard/shrink/Shrinker.java index 60524ba0d..758160cc9 100644 --- a/src/proguard/shrink/Shrinker.java +++ b/core/src/proguard/shrink/Shrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -79,12 +79,13 @@ public ClassPool execute(ClassPool programClassPool, }); ClassPoolVisitor classPoolvisitor = - ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep, - classUsageMarker, - usageMarker, - true, - false, - false); + new KeepClassSpecificationVisitorFactory(true, false, false) + .createClassPoolVisitor(configuration.keep, + classUsageMarker, + usageMarker, + usageMarker, + usageMarker); + // Mark the seeds. programClassPool.accept(classPoolvisitor); libraryClassPool.accept(classPoolvisitor); @@ -115,9 +116,12 @@ public ClassPool execute(ClassPool programClassPool, configuration.verbose); ClassPoolVisitor whyClassPoolvisitor = - ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.whyAreYouKeeping, - shortestUsagePrinter, - shortestUsagePrinter); + new ClassSpecificationVisitorFactory() + .createClassPoolVisitor(configuration.whyAreYouKeeping, + shortestUsagePrinter, + shortestUsagePrinter, + shortestUsagePrinter, + null); // Mark the seeds. programClassPool.accept(whyClassPoolvisitor); @@ -153,10 +157,9 @@ public ClassPool execute(ClassPool programClassPool, programClassPool.classesAccept( new UsedClassFilter(usageMarker, new MultiClassVisitor( - new ClassVisitor[] { new ClassShrinker(usageMarker), new ClassPoolFiller(newProgramClassPool) - }))); + ))); programClassPool.clear(); diff --git a/src/proguard/shrink/UsageMarker.java b/core/src/proguard/shrink/UsageMarker.java similarity index 91% rename from src/proguard/shrink/UsageMarker.java rename to core/src/proguard/shrink/UsageMarker.java index 8ebfaec41..acdfeda4f 100644 --- a/src/proguard/shrink/UsageMarker.java +++ b/core/src/proguard/shrink/UsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,8 @@ import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.module.*; +import proguard.classfile.attribute.module.visitor.*; import proguard.classfile.attribute.preverification.*; import proguard.classfile.attribute.preverification.visitor.*; import proguard.classfile.attribute.visitor.*; @@ -54,6 +56,10 @@ class UsageMarker ParameterInfoVisitor, LocalVariableInfoVisitor, LocalVariableTypeInfoVisitor, + RequiresInfoVisitor, + ExportsInfoVisitor, + OpensInfoVisitor, + ProvidesInfoVisitor, // AnnotationVisitor, // ElementValueVisitor, InstructionVisitor @@ -458,6 +464,15 @@ public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) } + public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) + { + if (shouldBeMarkedAsUsed(primitiveArrayConstant)) + { + markAsUsed(primitiveArrayConstant); + } + } + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { if (shouldBeMarkedAsUsed(stringConstant)) @@ -571,6 +586,28 @@ public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTyp } + public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) + { + if (shouldBeMarkedAsUsed(moduleConstant)) + { + markAsUsed(moduleConstant); + + markConstant(clazz, moduleConstant.u2nameIndex); + } + } + + + public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) + { + if (shouldBeMarkedAsUsed(packageConstant)) + { + markAsUsed(packageConstant); + + markConstant(clazz, packageConstant.u2nameIndex); + } + } + + /** * This AttributeVisitor marks the bootstrap methods attributes, their * method entries, their method handles, and their arguments. @@ -688,6 +725,48 @@ public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute } + public void visitModuleAttribute(Clazz clazz, ModuleAttribute moduleAttribute) + { + markAsUsed(moduleAttribute); + + markConstant(clazz, moduleAttribute.u2attributeNameIndex); + markConstant(clazz, moduleAttribute.u2moduleNameIndex); + + if (moduleAttribute.u2moduleVersionIndex != 0) + { + markConstant(clazz, moduleAttribute.u2moduleVersionIndex); + } + moduleAttribute.requiresAccept(clazz, this); + moduleAttribute.exportsAccept(clazz, this); + moduleAttribute.opensAccept(clazz, this); + + for (int index = 0; index < moduleAttribute.u2usesCount; index++) + { + markConstant(clazz, moduleAttribute.u2uses[index]); + } + + moduleAttribute.providesAccept(clazz, this); + } + + + public void visitModuleMainClassAttribute(Clazz clazz, ModuleMainClassAttribute moduleMainClassAttribute) + { + markAsUsed(moduleMainClassAttribute); + + markConstant(clazz, moduleMainClassAttribute.u2attributeNameIndex); + markConstant(clazz, moduleMainClassAttribute.u2mainClass); + } + + + public void visitModulePackagesAttribute(Clazz clazz, ModulePackagesAttribute modulePackagesAttribute) + { + markAsUsed(modulePackagesAttribute); + + markConstant(clazz, modulePackagesAttribute.u2attributeNameIndex); + modulePackagesAttribute.packagesAccept(clazz, this); + } + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) { markAsUsed(deprecatedAttribute); @@ -931,10 +1010,7 @@ public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttrib public void visitParameterInfo(Clazz clazz, Method method, int parameterIndex, ParameterInfo parameterInfo) { - if (parameterInfo.u2nameIndex != 0) - { - markConstant(clazz, parameterInfo.u2nameIndex); - } + parameterInfo.nameConstantAccept (clazz, this); } @@ -956,6 +1032,53 @@ public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute } + // Implementations for RequiresInfoVisitor. + + public void visitRequiresInfo(Clazz clazz, RequiresInfo requiresInfo) + { + markConstant(clazz, requiresInfo.u2requiresIndex); + markConstant(clazz, requiresInfo.u2requiresVersionIndex); + } + + + // Implementations for ExportsInfoVisitor. + + public void visitExportsInfo(Clazz clazz, ExportsInfo exportsInfo) + { + markConstant(clazz, exportsInfo.u2exportsIndex); + + for (int index = 0; index < exportsInfo.u2exportsToCount; index++) + { + markConstant(clazz, exportsInfo.u2exportsToIndex[index]); + } + } + + + // Implementations for OpensInfoVisitor. + + public void visitOpensInfo(Clazz clazz, OpensInfo opensInfo) + { + markConstant(clazz, opensInfo.u2opensIndex); + + for (int index = 0; index < opensInfo.u2opensToCount; index++) + { + markConstant(clazz, opensInfo.u2opensToIndex[index]); + } + } + + + // Implementations for ProvidesInfoVisitor. + + public void visitProvidesInfo(Clazz clazz, ProvidesInfo providesInfo) + { + markConstant(clazz, providesInfo.u2providesIndex); + + for (int index = 0; index < providesInfo.u2providesWithCount; index++) + { + markConstant(clazz, providesInfo.u2providesWithIndex[index]); + } + } + // // Implementations for AnnotationVisitor. // // public void visitAnnotation(Clazz clazz, Annotation annotation) diff --git a/src/proguard/shrink/UsagePrinter.java b/core/src/proguard/shrink/UsagePrinter.java similarity index 99% rename from src/proguard/shrink/UsagePrinter.java rename to core/src/proguard/shrink/UsagePrinter.java index c333de137..f8325c43f 100644 --- a/src/proguard/shrink/UsagePrinter.java +++ b/core/src/proguard/shrink/UsagePrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/UsedClassFilter.java b/core/src/proguard/shrink/UsedClassFilter.java similarity index 97% rename from src/proguard/shrink/UsedClassFilter.java rename to core/src/proguard/shrink/UsedClassFilter.java index 421cfc962..8fc5e13bf 100644 --- a/src/proguard/shrink/UsedClassFilter.java +++ b/core/src/proguard/shrink/UsedClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/UsedMemberFilter.java b/core/src/proguard/shrink/UsedMemberFilter.java similarity index 98% rename from src/proguard/shrink/UsedMemberFilter.java rename to core/src/proguard/shrink/UsedMemberFilter.java index fa01a0674..828091561 100644 --- a/src/proguard/shrink/UsedMemberFilter.java +++ b/core/src/proguard/shrink/UsedMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/shrink/package.html b/core/src/proguard/shrink/package.html similarity index 100% rename from src/proguard/shrink/package.html rename to core/src/proguard/shrink/package.html diff --git a/src/proguard/util/AndMatcher.java b/core/src/proguard/util/AndMatcher.java similarity index 79% rename from src/proguard/util/AndMatcher.java rename to core/src/proguard/util/AndMatcher.java index 55a8dafd6..5bb774f7c 100644 --- a/src/proguard/util/AndMatcher.java +++ b/core/src/proguard/util/AndMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -32,6 +32,9 @@ public class AndMatcher extends StringMatcher private final StringMatcher matcher2; + /** + * Creates a new AndMatcher with the two given string matchers. + */ public AndMatcher(StringMatcher matcher1, StringMatcher matcher2) { this.matcher1 = matcher1; @@ -41,9 +44,10 @@ public AndMatcher(StringMatcher matcher1, StringMatcher matcher2) // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { - return matcher1.matches(string, offset, length) && - matcher2.matches(string, offset, length); + return matcher1.matches(string, beginOffset, endOffset) && + matcher2.matches(string, beginOffset, endOffset); } } diff --git a/src/proguard/util/ArrayUtil.java b/core/src/proguard/util/ArrayUtil.java similarity index 55% rename from src/proguard/util/ArrayUtil.java rename to core/src/proguard/util/ArrayUtil.java index 1c064f121..dc06db5d9 100644 --- a/src/proguard/util/ArrayUtil.java +++ b/core/src/proguard/util/ArrayUtil.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -28,6 +28,27 @@ */ 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(boolean[] array1, boolean[] 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. @@ -49,6 +70,27 @@ public static boolean equal(byte[] array1, byte[] array2, int size) } + /** + * 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(char[] array1, char[] 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. @@ -91,6 +133,69 @@ public static boolean equal(int[] array1, int[] array2, int size) } + /** + * 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(long[] array1, long[] 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(float[] array1, float[] 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(double[] array1, double[] 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. @@ -142,6 +247,32 @@ public static boolean equalOrNull(Object[] array1, Object[] array2, int size) } + /** + * 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(boolean[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. + if (array[index]) + { + hashCode ^= 1; + } + } + + return hashCode; + } + + /** * Returns a hash code for the elements of the given array. * @param array the array. @@ -154,6 +285,33 @@ public static int hashCode(byte[] array, int size) for (int index = 0; index < size; index++) { + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. + hashCode ^= array[index] & 0xff; + } + + 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(char[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. hashCode ^= array[index]; } @@ -173,7 +331,11 @@ public static int hashCode(short[] array, int size) for (int index = 0; index < size; index++) { - hashCode ^= array[index]; + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. + hashCode ^= array[index] & 0xffff; } return hashCode; @@ -192,6 +354,10 @@ public static int hashCode(int[] array, int size) for (int index = 0; index < size; index++) { + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. hashCode ^= array[index]; } @@ -199,6 +365,79 @@ public static int hashCode(int[] array, int size) } + /** + * 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(long[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. + long longBits = array[index]; + + hashCode ^= longBits | (longBits >>> 32); + } + + 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(float[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. + hashCode ^= Float.floatToRawIntBits(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(double[] array, int size) + { + int hashCode = 0; + + for (int index = 0; index < size; index++) + { + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. + long longBits = Double.doubleToRawLongBits(array[index]); + + hashCode ^= longBits | (longBits >>> 32); + } + + return hashCode; + } + + /** * Returns a hash code for the elements of the given array. * @param array the array. @@ -211,6 +450,10 @@ public static int hashCode(Object[] array, int size) for (int index = 0; index < size; index++) { + // Rotate the hash code. + hashCode = (hashCode << 1) | (hashCode >>> 31); + + // XOR the element. hashCode ^= array[index].hashCode(); } @@ -224,155 +467,684 @@ public static int hashCode(Object[] array, int size) * @param array the array. * @return a hash code. */ - public static int hashCodeOrNull(Object[] array) + public static int hashCodeOrNull(Object[] array) + { + return array == null ? 0 : hashCode(array, array.length); + } + + + /** + * Returns a hash code for the elements of the given array, or 0 if it is + * null. + * @param array the array. + * @param size the size of the array to be taken into account. + * @return a hash code. + */ + public static int hashCodeOrNull(Object[] array, int size) + { + return array == null ? 0 : hashCode(array, size); + } + + + /** + * 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(boolean[] array1, int size1, + boolean[] 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(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(char[] array1, int size1, + char[] 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(long[] array1, int size1, + long[] 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(float[] array1, int size1, + float[] 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(double[] array1, int size1, + double[] 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; + } + + + /** + * Returns a shallow copy of the given array, or null if the input is null. + * @param array the array. + * @return a shallow copy of the original array, or null if the array is null. + */ + public static T[] cloneOrNull(T[] array) + { + return array != null ? array.clone() : 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 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 initial 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 initial 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 char[] add(char[] array, int size, char 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 char[] insert(char[] array, int size, int index, char 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(char[] array, int size, int index) { - return array == null ? 0 : hashCode(array, array.length); + System.arraycopy(array, index + 1, + array, index, + array.length - index - 1); + + array[size - 1] = 0; } /** - * Returns a hash code for the elements of the given array, or 0 if it is - * null. + * Ensures the given array has a given size. * @param array the array. - * @param size the size of the array to be taken into account. - * @return a hash code. + * @param size the target size of the array. + * @return the original array, or a copy if it had to be extended. */ - public static int hashCodeOrNull(Object[] array, int size) + public static char[] extendArray(char[] array, int size) { - return array == null ? 0 : hashCode(array, size); + // Reuse the existing array if possible. + if (array.length >= size) + { + return array; + } + + // Otherwise create and initialize a new array. + char[] newArray = new char[size]; + + System.arraycopy(array, 0, + newArray, 0, + array.length); + + return newArray; } /** - * 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. + * Ensures the given array has a given size. + * @param array the array. + * @param size the target size of the array. + * @param initialValue the initial value of the elements. + * @return the original array, or a copy if it had to be + * extended. */ - public static int compare(byte[] array1, int size1, - byte[] array2, int size2) + public static char[] ensureArraySize(char[] array, + int size, + char initialValue) { - int minSize = Math.min(size1, size2); - - for (int index = 0; index < minSize; index++) + // Is the existing array large enough? + if (array.length >= size) { - if (array1[index] < array2[index]) - { - return -1; - } - else if (array1[index] > array2[index]) + // Reinitialize the existing array. + Arrays.fill(array, 0, size, initialValue); + } + else + { + // Otherwise create and initialize a new array. + array = new char[size]; + + if (initialValue != 0) { - return 1; + Arrays.fill(array, 0, size, initialValue); } } - return size1 < size2 ? -1 : - size1 == size2 ? 0 : - 1; + return array; } /** - * 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. + * 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 compare(short[] array1, int size1, - short[] array2, int size2) + public static short[] add(short[] array, int size, short element) { - int minSize = Math.min(size1, size2); + array = extendArray(array, size + 1); - for (int index = 0; index < minSize; index++) - { - if (array1[index] < array2[index]) - { - return -1; - } - else if (array1[index] > array2[index]) - { - return 1; - } - } + array[size] = element; - return size1 < size2 ? -1 : - size1 == size2 ? 0 : - 1; + return array; } /** - * 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. + * 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 compare(int[] array1, int size1, - int[] array2, int size2) + public static short[] insert(short[] array, int size, int index, short element) { - int minSize = Math.min(size1, size2); + array = extendArray(array, size + 1); - for (int index = 0; index < minSize; index++) - { - if (array1[index] < array2[index]) - { - return -1; - } - else if (array1[index] > array2[index]) - { - return 1; - } - } + // Move the last part. + System.arraycopy(array, index, + array, index + 1, + size - index); - return size1 < size2 ? -1 : - size1 == size2 ? 0 : - 1; + array[index] = element; + + return array; } /** - * 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. + * 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 int compare(Comparable[] array1, int size1, - Comparable[] array2, int size2) + public static void remove(short[] array, int size, int index) { - 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; - } - } + System.arraycopy(array, index + 1, + array, index, + array.length - index - 1); - return size1 < size2 ? -1 : - size1 == size2 ? 0 : - 1; + array[size - 1] = 0; } @@ -382,7 +1154,7 @@ public static int compare(Comparable[] array1, int size1, * @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) + public static short[] extendArray(short[] array, int size) { // Reuse the existing array if possible. if (array.length >= size) @@ -391,7 +1163,7 @@ public static boolean[] extendArray(boolean[] array, int size) } // Otherwise create and initialize a new array. - boolean[] newArray = new boolean[size]; + short[] newArray = new short[size]; System.arraycopy(array, 0, newArray, 0, @@ -409,9 +1181,9 @@ public static boolean[] extendArray(boolean[] array, int size) * @return the original array, or a copy if it had to be * extended. */ - public static boolean[] ensureArraySize(boolean[] array, - int size, - boolean initialValue) + public static short[] ensureArraySize(short[] array, + int size, + short initialValue) { // Is the existing array large enough? if (array.length >= size) @@ -422,9 +1194,9 @@ public static boolean[] ensureArraySize(boolean[] array, else { // Otherwise create and initialize a new array. - array = new boolean[size]; + array = new short[size]; - if (initialValue) + if (initialValue != 0) { Arrays.fill(array, 0, size, initialValue); } @@ -442,7 +1214,7 @@ public static boolean[] ensureArraySize(boolean[] 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) + public static int[] add(int[] array, int size, int element) { array = extendArray(array, size + 1); @@ -461,7 +1233,7 @@ public static byte[] add(byte[] array, int size, byte element) * @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) + public static int[] insert(int[] array, int size, int index, int element) { array = extendArray(array, size + 1); @@ -482,7 +1254,7 @@ public static byte[] insert(byte[] array, int size, int index, byte element) * @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) + public static void remove(int[] array, int size, int index) { System.arraycopy(array, index + 1, array, index, @@ -498,7 +1270,7 @@ public static void remove(byte[] array, int size, int index) * @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) + public static int[] extendArray(int[] array, int size) { // Reuse the existing array if possible. if (array.length >= size) @@ -507,7 +1279,7 @@ public static byte[] extendArray(byte[] array, int size) } // Otherwise create and initialize a new array. - byte[] newArray = new byte[size]; + int[] newArray = new int[size]; System.arraycopy(array, 0, newArray, 0, @@ -525,9 +1297,9 @@ public static byte[] extendArray(byte[] array, int size) * @return the original array, or a copy if it had to be * extended. */ - public static byte[] ensureArraySize(byte[] array, - int size, - byte initialValue) + public static int[] ensureArraySize(int[] array, + int size, + int initialValue) { // Is the existing array large enough? if (array.length >= size) @@ -538,7 +1310,7 @@ public static byte[] ensureArraySize(byte[] array, else { // Otherwise create and initialize a new array. - array = new byte[size]; + array = new int[size]; if (initialValue != 0) { @@ -558,7 +1330,7 @@ public static byte[] ensureArraySize(byte[] 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) + public static long[] add(long[] array, int size, long element) { array = extendArray(array, size + 1); @@ -577,7 +1349,7 @@ public static short[] add(short[] array, int size, short element) * @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) + public static long[] insert(long[] array, int size, int index, long element) { array = extendArray(array, size + 1); @@ -598,7 +1370,7 @@ public static short[] insert(short[] array, int size, int index, short element) * @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) + public static void remove(long[] array, int size, int index) { System.arraycopy(array, index + 1, array, index, @@ -614,7 +1386,7 @@ public static void remove(short[] array, int size, int index) * @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) + public static long[] extendArray(long[] array, int size) { // Reuse the existing array if possible. if (array.length >= size) @@ -623,7 +1395,7 @@ public static short[] extendArray(short[] array, int size) } // Otherwise create and initialize a new array. - short[] newArray = new short[size]; + long[] newArray = new long[size]; System.arraycopy(array, 0, newArray, 0, @@ -641,9 +1413,9 @@ public static short[] extendArray(short[] array, int size) * @return the original array, or a copy if it had to be * extended. */ - public static short[] ensureArraySize(short[] array, - int size, - short initialValue) + public static long[] ensureArraySize(long[] array, + int size, + long initialValue) { // Is the existing array large enough? if (array.length >= size) @@ -654,9 +1426,9 @@ public static short[] ensureArraySize(short[] array, else { // Otherwise create and initialize a new array. - array = new short[size]; + array = new long[size]; - if (initialValue != 0) + if (initialValue != 0L) { Arrays.fill(array, 0, size, initialValue); } @@ -674,7 +1446,7 @@ public static short[] ensureArraySize(short[] 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) + public static float[] add(float[] array, int size, float element) { array = extendArray(array, size + 1); @@ -693,7 +1465,7 @@ public static int[] add(int[] array, int size, int element) * @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) + public static float[] insert(float[] array, int size, int index, float element) { array = extendArray(array, size + 1); @@ -714,7 +1486,7 @@ public static int[] insert(int[] array, int size, int index, int element) * @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) + public static void remove(float[] array, int size, int index) { System.arraycopy(array, index + 1, array, index, @@ -730,7 +1502,7 @@ public static void remove(int[] array, int size, int index) * @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) + public static float[] extendArray(float[] array, int size) { // Reuse the existing array if possible. if (array.length >= size) @@ -739,7 +1511,7 @@ public static int[] extendArray(int[] array, int size) } // Otherwise create and initialize a new array. - int[] newArray = new int[size]; + float[] newArray = new float[size]; System.arraycopy(array, 0, newArray, 0, @@ -757,9 +1529,9 @@ public static int[] extendArray(int[] array, int size) * @return the original array, or a copy if it had to be * extended. */ - public static int[] ensureArraySize(int[] array, - int size, - int initialValue) + public static float[] ensureArraySize(float[] array, + int size, + float initialValue) { // Is the existing array large enough? if (array.length >= size) @@ -770,7 +1542,7 @@ public static int[] ensureArraySize(int[] array, else { // Otherwise create and initialize a new array. - array = new int[size]; + array = new float[size]; if (initialValue != 0) { @@ -790,7 +1562,7 @@ public static int[] ensureArraySize(int[] 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) + public static double[] add(double[] array, int size, double element) { array = extendArray(array, size + 1); @@ -809,7 +1581,7 @@ public static long[] add(long[] array, int size, long element) * @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) + public static double[] insert(double[] array, int size, int index, double element) { array = extendArray(array, size + 1); @@ -830,7 +1602,7 @@ public static long[] insert(long[] array, int size, int index, long element) * @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) + public static void remove(double[] array, int size, int index) { System.arraycopy(array, index + 1, array, index, @@ -846,7 +1618,7 @@ public static void remove(long[] array, int size, int index) * @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) + public static double[] extendArray(double[] array, int size) { // Reuse the existing array if possible. if (array.length >= size) @@ -855,7 +1627,7 @@ public static long[] extendArray(long[] array, int size) } // Otherwise create and initialize a new array. - long[] newArray = new long[size]; + double[] newArray = new double[size]; System.arraycopy(array, 0, newArray, 0, @@ -873,9 +1645,9 @@ public static long[] extendArray(long[] array, int size) * @return the original array, or a copy if it had to be * extended. */ - public static long[] ensureArraySize(long[] array, + public static double[] ensureArraySize(double[] array, int size, - long initialValue) + double initialValue) { // Is the existing array large enough? if (array.length >= size) @@ -886,9 +1658,9 @@ public static long[] ensureArraySize(long[] array, else { // Otherwise create and initialize a new array. - array = new long[size]; + array = new double[size]; - if (initialValue != 0L) + if (initialValue != 0) { Arrays.fill(array, 0, size, initialValue); } @@ -906,7 +1678,7 @@ public static long[] ensureArraySize(long[] 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) + public static T[] add(T[] array, int size, T element) { array = extendArray(array, size + 1); @@ -925,7 +1697,7 @@ public static Object[] add(Object[] array, int size, Object element) * @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) + public static T[] insert(T[] array, int size, int index, T element) { array = extendArray(array, size + 1); @@ -962,7 +1734,7 @@ public static void remove(Object[] array, int size, int index) * @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) + public static T[] extendArray(T[] array, int size) { // Reuse the existing array if possible. if (array.length >= size) @@ -971,7 +1743,7 @@ public static Object[] extendArray(Object[] array, int size) } // Otherwise create and initialize a new array. - Object[] newArray = (Object[])Array.newInstance(array.getClass().getComponentType(), size); + T[] newArray = (T[])Array.newInstance(array.getClass().getComponentType(), size); System.arraycopy(array, 0, newArray, 0, @@ -989,9 +1761,9 @@ public static Object[] extendArray(Object[] array, int size) * @return the original array, or a copy if it had to be * extended. */ - public static Object[] ensureArraySize(Object[] array, - int size, - Object initialValue) + public static T[] ensureArraySize(T[] array, + int size, + T initialValue) { // Is the existing array large enough? if (array.length >= size) @@ -1002,7 +1774,7 @@ public static Object[] ensureArraySize(Object[] array, else { // Otherwise create and initialize a new array. - array = (Object[])Array.newInstance(array.getClass().getComponentType(), size); + array = (T[])Array.newInstance(array.getClass().getComponentType(), size); if (initialValue != null) { diff --git a/core/src/proguard/util/ClassNameParser.java b/core/src/proguard/util/ClassNameParser.java new file mode 100644 index 000000000..5106ba8a3 --- /dev/null +++ b/core/src/proguard/util/ClassNameParser.java @@ -0,0 +1,364 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.util; + +import proguard.classfile.ClassConstants; + +import java.util.*; + +/** + * 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), + * 'L///;' for any number of internal types (class names and primitive + * types), and + * '' for a reference to an earlier wildcard (n = 1, 2, ...) + * + * @author Eric Lafortune + */ +public class ClassNameParser implements StringParser +{ + private static final char[] PRIMITIVE_TYPES = new char[] + { + ClassConstants.TYPE_VOID, + ClassConstants.TYPE_BOOLEAN, + ClassConstants.TYPE_BYTE, + ClassConstants.TYPE_CHAR, + ClassConstants.TYPE_SHORT, + ClassConstants.TYPE_INT, + ClassConstants.TYPE_LONG, + ClassConstants.TYPE_FLOAT, + ClassConstants.TYPE_DOUBLE, + }; + + + private List variableStringMatchers; + + + /** + * Creates a new ClassNameParser. + */ + public ClassNameParser() + { + this(null); + } + + + /** + * Creates a new ClassNameParser that supports references to earlier + * wildcards. + * + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + */ + public ClassNameParser(List variableStringMatchers) + { + this.variableStringMatchers = variableStringMatchers; + } + + + // Implementations for StringParser. + + public StringMatcher parse(String regularExpression) + { + int index; + StringMatcher nextMatcher = new EmptyStringMatcher(); + + // Look for wildcards. + for (index = 0; index < regularExpression.length(); index++) + { + int wildCardIndex; + + // Is there an 'L///;' wildcard? + if (regularExpression.regionMatches(index, "L///;", 0, 5)) + { + SettableMatcher settableMatcher = new SettableMatcher(); + + // Create a matcher for the wildcard. + nextMatcher = rememberVariableStringMatcher( + new VariableStringMatcher(null, + new char[] { ClassConstants.METHOD_ARGUMENTS_CLOSE }, + 0, + Integer.MAX_VALUE, + settableMatcher)); + + settableMatcher.setMatcher(parse(regularExpression.substring(index + 5))); + break; + } + + // Is there an 'L***;' wildcard? + else if (regularExpression.regionMatches(index, "L***;", 0, 5)) + { + SettableMatcher settableMatcher = new SettableMatcher(); + + // Create a matcher for the wildcard. + // TODO: The returned variable matcher is actually a composite that doesn't return the entire matched string. + nextMatcher = rememberVariableStringMatcher( + createAnyTypeMatcher(settableMatcher)); + + // Recursively create a matcher for the rest of the string. + settableMatcher.setMatcher(parse(regularExpression.substring(index + 5))); + break; + } + + // Is there a '**' wildcard? + else if (regularExpression.regionMatches(index, "**", 0, 2)) + { + // Handle the end of the regular expression more efficiently, + // without any next matcher for the variable string matcher. + SettableMatcher settableMatcher = + index + 2 == regularExpression.length() ? null : + new SettableMatcher(); + + // Create a matcher for the wildcard. + nextMatcher = rememberVariableStringMatcher( + new VariableStringMatcher(null, + new char[] { ClassConstants.TYPE_CLASS_END }, + 0, + Integer.MAX_VALUE, + settableMatcher)); + + // Recursively create a matcher for the rest of the string. + if (settableMatcher != null) + { + settableMatcher.setMatcher(parse(regularExpression.substring(index + 2))); + } + break; + } + + // Is there a '*' wildcard? + else if (regularExpression.charAt(index) == '*') + { + SettableMatcher settableMatcher = new SettableMatcher(); + + // Create a matcher for the wildcard. + nextMatcher = rememberVariableStringMatcher( + new VariableStringMatcher(null, + new char[] { ClassConstants.TYPE_CLASS_END, ClassConstants.PACKAGE_SEPARATOR }, + 0, + Integer.MAX_VALUE, + settableMatcher)); + + // Recursively create a matcher for the rest of the string. + settableMatcher.setMatcher(parse(regularExpression.substring(index + 1))); + break; + } + + // Is there a '?' wildcard? + else if (regularExpression.charAt(index) == '?') + { + SettableMatcher settableMatcher = new SettableMatcher(); + + // Create a matcher for the wildcard. + nextMatcher = rememberVariableStringMatcher( + new VariableStringMatcher(null, + new char[] { ClassConstants.TYPE_CLASS_END, ClassConstants.PACKAGE_SEPARATOR }, + 1, + 1, + settableMatcher)); + + // Recursively create a matcher for the rest of the string. + settableMatcher.setMatcher(parse(regularExpression.substring(index + 1))); + break; + } + + // Is there a '%' wildcard? + else if (regularExpression.charAt(index) == '%') + { + SettableMatcher settableMatcher = new SettableMatcher(); + + // Create a matcher for the wildcard. + nextMatcher = rememberVariableStringMatcher( + new VariableStringMatcher(PRIMITIVE_TYPES, + null, + 1, + 1, + settableMatcher)); + + // Recursively create a matcher for the rest of the string. + settableMatcher.setMatcher(parse(regularExpression.substring(index + 1))); + break; + } + + // Is there a '' wildcard? + else if ((wildCardIndex = wildCardIndex(regularExpression, index)) > 0) + { + // Find the index of the closing bracket again. + int closingIndex = regularExpression.indexOf('>', index + 1); + + // Retrieve the specified variable string matcher and + // recursively create a matcher for the rest of the string. + nextMatcher = + new MatchedStringMatcher(retrieveVariableStringMatcher(wildCardIndex - 1), + parse(regularExpression.substring(closingIndex + 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 + // Any number of '['. + new VariableStringMatcher(new char[] { ClassConstants.TYPE_ARRAY }, + null, + 0, + 255, + // Followed by: + new OrMatcher( + // A primitive type. + new VariableStringMatcher(PRIMITIVE_TYPES, + null, + 1, + 1, + nextMatcher), + + // Or a class type. + new VariableStringMatcher(new char[] { ClassConstants.TYPE_CLASS_START }, + null, + 1, + 1, + new VariableStringMatcher(null, + new char[] { ClassConstants.TYPE_CLASS_END }, + 0, + Integer.MAX_VALUE, + new VariableStringMatcher(new char[] { ClassConstants.TYPE_CLASS_END }, + null, + 1, + 1, + nextMatcher))))); + } + + + /** + * Adds the given variable string matcher to the list of string matchers. + */ + private VariableStringMatcher rememberVariableStringMatcher(VariableStringMatcher variableStringMatcher) + { + if (variableStringMatchers != null) + { + variableStringMatchers.add(variableStringMatcher); + } + + return variableStringMatcher; + } + + + /** + * Retrieves the specified variable string matcher from the list of string + * matchers. + */ + private VariableStringMatcher retrieveVariableStringMatcher(int index) + { + return (VariableStringMatcher) variableStringMatchers.get(index); + } + + + /** + * Parses a reference to a wildcard at the given index, if any. + * Returns the 1-based index, or 0 otherwise. + */ + private int wildCardIndex(String string, int index) + throws IllegalArgumentException + { + if (string.charAt(index) != '<') + { + return 0; + } + + int closingBracketIndex = string.indexOf('>', index); + if (closingBracketIndex < 0) + { + throw new IllegalArgumentException("Missing closing angular bracket"); + } + + if (variableStringMatchers == null) + { + System.err.println(string); + throw new IllegalArgumentException("References to wildcards are not supported in this argument"); + } + + String argumentBetweenBrackets = string.substring(index+1, closingBracketIndex); + + try + { + int wildcardIndex = Integer.parseInt(argumentBetweenBrackets); + if (wildcardIndex < 1 || + wildcardIndex > variableStringMatchers.size()) + { + throw new IllegalArgumentException("Invalid reference to wildcard ("+wildcardIndex+", must lie between 1 and "+variableStringMatchers.size()+")"); + } + + return wildcardIndex; + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException("Reference to wildcard must be a number ("+argumentBetweenBrackets+")"); + } + } + + + /** + * 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/core/src/proguard/util/CollectionMatcher.java b/core/src/proguard/util/CollectionMatcher.java new file mode 100644 index 000000000..486a44db3 --- /dev/null +++ b/core/src/proguard/util/CollectionMatcher.java @@ -0,0 +1,55 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.Set; + +/** + * This matcher tests whether strings match with a String in a given Set. + * + * @author Johan Leys + */ +public class CollectionMatcher extends StringMatcher +{ + private final Set set; + + + public CollectionMatcher(Set set) + { + this.set = set; + } + + + // Implementations for StringMatcher. + + @Override + public boolean matches(String string) + { + return set.contains(string); + } + + + @Override + protected boolean matches(String string, int beginOffset, int endOffset) + { + return set.contains(string.substring(beginOffset, endOffset)); + } +} diff --git a/src/proguard/util/ConstantMatcher.java b/core/src/proguard/util/ConstantMatcher.java similarity index 90% rename from src/proguard/util/ConstantMatcher.java rename to core/src/proguard/util/ConstantMatcher.java index b640eca60..85d744dda 100644 --- a/src/proguard/util/ConstantMatcher.java +++ b/core/src/proguard/util/ConstantMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -41,7 +41,8 @@ public ConstantMatcher(boolean matches) // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { return matches; } diff --git a/core/src/proguard/util/Counter.java b/core/src/proguard/util/Counter.java new file mode 100644 index 000000000..d92f4f080 --- /dev/null +++ b/core/src/proguard/util/Counter.java @@ -0,0 +1,34 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 provides a counter that can be retrieved. + * + * @author Eric Lafortune + */ +public interface Counter +{ + /** + * Returns the current value of the counter. + */ + public int getCount(); +} diff --git a/src/proguard/util/EmptyStringMatcher.java b/core/src/proguard/util/EmptyStringMatcher.java similarity index 85% rename from src/proguard/util/EmptyStringMatcher.java rename to core/src/proguard/util/EmptyStringMatcher.java index a18315717..60bd0fd6d 100644 --- a/src/proguard/util/EmptyStringMatcher.java +++ b/core/src/proguard/util/EmptyStringMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -29,8 +29,9 @@ public class EmptyStringMatcher extends StringMatcher { // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { - return length == 0; + return beginOffset == endOffset; } } diff --git a/src/proguard/util/ExtensionMatcher.java b/core/src/proguard/util/ExtensionMatcher.java similarity index 73% rename from src/proguard/util/ExtensionMatcher.java rename to core/src/proguard/util/ExtensionMatcher.java index 94cca054c..41ac8931b 100644 --- a/src/proguard/util/ExtensionMatcher.java +++ b/core/src/proguard/util/ExtensionMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -33,6 +33,7 @@ public class ExtensionMatcher extends StringMatcher /** * Creates a new StringMatcher. + * * @param extension the extension against which strings will be matched. */ public ExtensionMatcher(String extension) @@ -43,9 +44,10 @@ public ExtensionMatcher(String extension) // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { - return endsWithIgnoreCase(string, offset, length, extension); + return endsWithIgnoreCase(string, beginOffset, endOffset, extension); } @@ -53,10 +55,13 @@ protected boolean matches(String string, int offset, int length) * Returns whether the given string ends with the given suffix, ignoring its * case. */ - private static boolean endsWithIgnoreCase(String string, int offset, int length, String suffix) + private static boolean endsWithIgnoreCase(String string, + int beginOffset, + int endOffset, + String suffix) { int suffixLength = suffix.length(); - return string.regionMatches(true, offset + length - suffixLength, suffix, 0, suffixLength); + return string.regionMatches(true, endOffset - suffixLength, suffix, 0, suffixLength); } } diff --git a/src/proguard/util/FileNameParser.java b/core/src/proguard/util/FileNameParser.java similarity index 98% rename from src/proguard/util/FileNameParser.java rename to core/src/proguard/util/FileNameParser.java index 144ff578a..aca322562 100644 --- a/src/proguard/util/FileNameParser.java +++ b/core/src/proguard/util/FileNameParser.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/util/FixedStringMatcher.java b/core/src/proguard/util/FixedStringMatcher.java similarity index 65% rename from src/proguard/util/FixedStringMatcher.java rename to core/src/proguard/util/FixedStringMatcher.java index d8e7bf7d9..021812869 100644 --- a/src/proguard/util/FixedStringMatcher.java +++ b/core/src/proguard/util/FixedStringMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -32,12 +32,24 @@ public class FixedStringMatcher extends StringMatcher private final StringMatcher nextMatcher; + /** + * Creates a new FixedStringMatcher. + * + * @param fixedString the string to match. + */ public FixedStringMatcher(String fixedString) { this(fixedString, null); } + /** + * Creates a new FixedStringMatcher. + * + * @param fixedString the string prefix to match. + * @param nextMatcher an optional string matcher to match the remainder of + * the string. + */ public FixedStringMatcher(String fixedString, StringMatcher nextMatcher) { this.fixedString = fixedString; @@ -47,14 +59,16 @@ public FixedStringMatcher(String fixedString, StringMatcher nextMatcher) // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { + int stringLength = endOffset - beginOffset; int fixedStringLength = fixedString.length(); - return length >= fixedStringLength && - string.startsWith(fixedString, offset) && - (nextMatcher == null || + return stringLength >= fixedStringLength && + string.startsWith(fixedString, beginOffset) && + ((nextMatcher == null && stringLength == fixedStringLength) || nextMatcher.matches(string, - offset + fixedStringLength, - length - fixedStringLength)); + beginOffset + fixedStringLength, + endOffset)); } } diff --git a/src/proguard/util/ListMatcher.java b/core/src/proguard/util/ListMatcher.java similarity index 88% rename from src/proguard/util/ListMatcher.java rename to core/src/proguard/util/ListMatcher.java index bdf1b904d..ffc88a2d0 100644 --- a/src/proguard/util/ListMatcher.java +++ b/core/src/proguard/util/ListMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -34,7 +34,7 @@ public class ListMatcher extends StringMatcher private final boolean[] negate; - public ListMatcher(StringMatcher[] matchers) + public ListMatcher(StringMatcher... matchers) { this(matchers, null); } @@ -49,13 +49,14 @@ public ListMatcher(StringMatcher[] matchers, boolean[] negate) // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { // Check the list of matchers. for (int index = 0; index < matchers.length; index++) { StringMatcher matcher = matchers[index]; - if (matcher.matches(string, offset, length)) + if (matcher.matches(string, beginOffset, endOffset)) { return negate == null || !negate[index]; diff --git a/src/proguard/util/ListParser.java b/core/src/proguard/util/ListParser.java similarity index 98% rename from src/proguard/util/ListParser.java rename to core/src/proguard/util/ListParser.java index 91448b2ee..9acba699c 100644 --- a/src/proguard/util/ListParser.java +++ b/core/src/proguard/util/ListParser.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/util/ListUtil.java b/core/src/proguard/util/ListUtil.java similarity index 98% rename from src/proguard/util/ListUtil.java rename to core/src/proguard/util/ListUtil.java index 6b63669cb..303c00aed 100644 --- a/src/proguard/util/ListUtil.java +++ b/core/src/proguard/util/ListUtil.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/util/MatchedStringMatcher.java b/core/src/proguard/util/MatchedStringMatcher.java new file mode 100644 index 000000000..5cd862c9d --- /dev/null +++ b/core/src/proguard/util/MatchedStringMatcher.java @@ -0,0 +1,72 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 optional given StringMatcher. + * + * @see VariableStringMatcher + * @author Eric Lafortune + */ +public class MatchedStringMatcher extends StringMatcher +{ + private final VariableStringMatcher variableStringMatcher; + private final StringMatcher nextMatcher; + + + /** + * Creates a new MatchedStringMatcher + * + * @param variableStringMatcher the variable string matcher that can + * provide the string to match. + * @param nextMatcher an optional string matcher to match the + * remainder of the string. + */ + public MatchedStringMatcher(VariableStringMatcher variableStringMatcher, + StringMatcher nextMatcher) + { + this.variableStringMatcher = variableStringMatcher; + this.nextMatcher = nextMatcher; + } + + + // Implementation for StringMatcher. + + @Override + protected boolean matches(String string, int beginOffset, int endOffset) + { + String matchingString = variableStringMatcher.getMatchingString(); + if (matchingString == null) + { + return false; + } + + int stringLength = endOffset - beginOffset; + int matchngStringLength = matchingString.length(); + return stringLength >= matchngStringLength && + string.startsWith(matchingString, beginOffset) && + ((nextMatcher == null && stringLength == matchngStringLength) || + nextMatcher.matches(string, + beginOffset + matchngStringLength, + endOffset)); + } +} diff --git a/core/src/proguard/util/MultiValueMap.java b/core/src/proguard/util/MultiValueMap.java new file mode 100644 index 000000000..a4e2e0c63 --- /dev/null +++ b/core/src/proguard/util/MultiValueMap.java @@ -0,0 +1,92 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.*; + +/** + * A key-values map that can have multiple values associated with each key. + * + * There is an efficient lookup method to retrieve all values of all keys. + * + * @param the key type + * @param the value type + * + * @author Johan Leys + */ +public class MultiValueMap +{ + private final Map> keyValueMap = new HashMap>(); + + private final Set values = new HashSet(); + + + public void put(K key, V value) + { + putAll(key, Collections.singleton(value)); + } + + + public void putAll(Set key, V value) + { + putAll(key, Collections.singleton(value)); + } + + + public void putAll(Set keys, Set values) + { + for (K key : keys) + { + putAll(key, values); + } + } + + + public void putAll(K key, Set values) + { + this.values.addAll(values); + Set existingValues = keyValueMap.get(key); + if (existingValues == null) + { + existingValues = new HashSet(); + keyValueMap.put(key, existingValues); + } + existingValues.addAll(values); + } + + + public Set get(K key) + { + return keyValueMap.get(key); + } + + + /** + * Returns a Set with all values of all keys. + * + * @return a Set with all values of all keys. + */ + public Set getValues() + { + return values; + } +} diff --git a/core/src/proguard/util/NameParser.java b/core/src/proguard/util/NameParser.java new file mode 100644 index 000000000..dd718999f --- /dev/null +++ b/core/src/proguard/util/NameParser.java @@ -0,0 +1,224 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 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. + * The regular expressions can contain the following wildcards: + * '?' for a single character, + * '*' for any number of characters, and + * '' for a reference to an earlier wildcard (n = 1, 2, ...) + * + * @author Eric Lafortune + */ +public class NameParser implements StringParser +{ + private List variableStringMatchers; + + + /** + * Creates a new NameParser. + */ + public NameParser() + { + this(null); + } + + + /** + * Creates a new NameParser that supports references to earlier + * wildcards. + * + * @param variableStringMatchers an optional mutable list of + * VariableStringMatcher instances that match + * the wildcards. + */ + public NameParser(List variableStringMatchers) + { + this.variableStringMatchers = variableStringMatchers; + } + + + // Implementations for StringParser. + + public StringMatcher parse(String regularExpression) + { + int index; + StringMatcher nextMatcher = new EmptyStringMatcher(); + + // Look for wildcards. + for (index = 0; index < regularExpression.length(); index++) + { + int wildCardIndex; + + // Is there a '*' wildcard? + if (regularExpression.charAt(index) == '*') + { + SettableMatcher settableMatcher = new SettableMatcher(); + + // Create a matcher for the wildcard. + nextMatcher = rememberVariableStringMatcher( + new VariableStringMatcher(null, + null, + 0, + Integer.MAX_VALUE, + settableMatcher)); + + // Recursively create a matcher for the rest of the string. + settableMatcher.setMatcher(parse(regularExpression.substring(index + 1))); + break; + } + + // Is there a '?' wildcard? + else if (regularExpression.charAt(index) == '?') + { + SettableMatcher settableMatcher = new SettableMatcher(); + + // Create a matcher for the wildcard. + nextMatcher = rememberVariableStringMatcher( + new VariableStringMatcher(null, + null, + 1, + 1, + settableMatcher)); + + // Recursively create a matcher for the rest of the string. + settableMatcher.setMatcher(parse(regularExpression.substring(index + 1))); + break; + } + + // Is there a '' wildcard? + else if ((wildCardIndex = wildCardIndex(regularExpression, index)) > 0) + { + // Find the index of the closing bracket again. + int closingIndex = regularExpression.indexOf('>', index + 1); + + // Retrieve the specified variable string matcher and + // recursively create a matcher for the rest of the string. + nextMatcher = + new MatchedStringMatcher(retrieveVariableStringMatcher(wildCardIndex - 1), + parse(regularExpression.substring(closingIndex + 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. + + /** + * Parses a reference to a wildcard at the given index, if any. + * Returns the 1-based index, or 0 otherwise. + */ + private int wildCardIndex(String string, int index) + throws IllegalArgumentException + { + if (variableStringMatchers == null || + string.charAt(index) != '<') + { + return 0; + } + + int closingBracketIndex = string.indexOf('>', index); + if (closingBracketIndex < 0) + { + throw new IllegalArgumentException("Missing closing angular bracket"); + } + + String argumentBetweenBrackets = string.substring(index+1, closingBracketIndex); + + try + { + int wildcardIndex = Integer.parseInt(argumentBetweenBrackets); + if (wildcardIndex < 1 || + wildcardIndex > variableStringMatchers.size()) + { + throw new IllegalArgumentException("Invalid reference to wildcard ("+wildcardIndex+", must lie between 1 and "+variableStringMatchers.size()+")"); + } + + return wildcardIndex; + } + catch (NumberFormatException e) + { + return 0; + } + } + + + /** + * Adds the given variable string matcher to the list of string matchers. + */ + private VariableStringMatcher rememberVariableStringMatcher(VariableStringMatcher variableStringMatcher) + { + if (variableStringMatchers != null) + { + variableStringMatchers.add(variableStringMatcher); + } + + return variableStringMatcher; + } + + + /** + * Retrieves the specified variable string matcher from the list of string + * matchers. + */ + private VariableStringMatcher retrieveVariableStringMatcher(int index) + { + return (VariableStringMatcher)variableStringMatchers.get(index); + } + + + /** + * 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/core/src/proguard/util/NotMatcher.java similarity index 85% rename from src/proguard/util/NotMatcher.java rename to core/src/proguard/util/NotMatcher.java index 8f67b85e2..ab5ee4c76 100644 --- a/src/proguard/util/NotMatcher.java +++ b/core/src/proguard/util/NotMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -39,8 +39,9 @@ public NotMatcher(StringMatcher matcher) // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { - return !matcher.matches(string, offset, length); + return !matcher.matches(string, beginOffset, endOffset); } } diff --git a/src/proguard/util/ObjectUtil.java b/core/src/proguard/util/ObjectUtil.java similarity index 97% rename from src/proguard/util/ObjectUtil.java rename to core/src/proguard/util/ObjectUtil.java index c698fef99..a3e9de455 100644 --- a/src/proguard/util/ObjectUtil.java +++ b/core/src/proguard/util/ObjectUtil.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/util/OrMatcher.java b/core/src/proguard/util/OrMatcher.java similarity index 79% rename from src/proguard/util/OrMatcher.java rename to core/src/proguard/util/OrMatcher.java index 953f6b908..5017836ef 100644 --- a/src/proguard/util/OrMatcher.java +++ b/core/src/proguard/util/OrMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -32,6 +32,9 @@ public class OrMatcher extends StringMatcher private final StringMatcher matcher2; + /** + * Creates a new OrMatcher with the two given string matchers. + */ public OrMatcher(StringMatcher matcher1, StringMatcher matcher2) { this.matcher1 = matcher1; @@ -41,9 +44,10 @@ public OrMatcher(StringMatcher matcher1, StringMatcher matcher2) // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { - return matcher1.matches(string, offset, length) || - matcher2.matches(string, offset, length); + return matcher1.matches(string, beginOffset, endOffset) || + matcher2.matches(string, beginOffset, endOffset); } } diff --git a/core/src/proguard/util/PrintWriterUtil.java b/core/src/proguard/util/PrintWriterUtil.java new file mode 100644 index 000000000..f9a226c3b --- /dev/null +++ b/core/src/proguard/util/PrintWriterUtil.java @@ -0,0 +1,136 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.Configuration; + +import java.io.*; + +/** + * Utility code for creating PrintWriters for printing mappings etc. + * @author Johan Leys + */ +public class PrintWriterUtil +{ + /** + * Returns a print writer for the given file, or the standard output if + * the file name is empty. + */ + public static PrintWriter createPrintWriterOut(File outputFile) + throws FileNotFoundException, UnsupportedEncodingException + { + return createPrintWriterOut(outputFile, false); + } + + /** + * Returns a print writer for the given file, or the standard output if + * the file name is empty. + */ + public static PrintWriter createPrintWriterOut(File outputFile, boolean append) + throws FileNotFoundException, UnsupportedEncodingException + { + + return createPrintWriter(outputFile, new PrintWriter(System.out, true), append); + } + + + /** + * Returns a print writer for the given file, or the standard output if + * the file name is empty. + */ + public static PrintWriter createPrintWriterErr(File outputFile) + throws FileNotFoundException, UnsupportedEncodingException + { + return createPrintWriter(outputFile, new PrintWriter(System.err, true)); + } + + + /** + * Returns a print writer for the given file, or the standard output if + * the file name is empty. + */ + public static PrintWriter createPrintWriter(File outputFile, PrintWriter console) + throws FileNotFoundException, UnsupportedEncodingException + { + return createPrintWriter(outputFile, console, false); + } + + + /** + * Returns a print writer for the given file, or the standard output if + * the file name is empty. + */ + public static PrintWriter createPrintWriter(File outputFile, + PrintWriter console, + boolean append) + throws FileNotFoundException, UnsupportedEncodingException + { + return outputFile == Configuration.STD_OUT ? + console : + new PrintWriter( + new BufferedWriter( + new OutputStreamWriter( + new FileOutputStream(outputFile, append), "UTF-8"))); + } + + /** + * Closes the given print writer, or flushes it if is the standard output. + */ + public static void closePrintWriter(File file, PrintWriter printWriter) + { + if (file == Configuration.STD_OUT) + { + printWriter.flush(); + } + else + { + printWriter.close(); + } + } + + + /** + * Returns the canonical file name for the given file, or "standard output" + * if the file name is empty. + */ + public static String fileName(File file) + { + if (file == Configuration.STD_OUT) + { + return "standard output"; + } + else + { + try + { + return file.getCanonicalPath(); + } + catch (IOException ex) + { + return file.getPath(); + } + } + } + + + // Hide constructor for util class. + private PrintWriterUtil() {} +} diff --git a/src/proguard/util/SettableMatcher.java b/core/src/proguard/util/SettableMatcher.java similarity index 86% rename from src/proguard/util/SettableMatcher.java rename to core/src/proguard/util/SettableMatcher.java index 6a2093b52..7d640fee4 100644 --- a/src/proguard/util/SettableMatcher.java +++ b/core/src/proguard/util/SettableMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -39,8 +39,9 @@ public void setMatcher(StringMatcher matcher) // Implementations for StringMatcher. - protected boolean matches(String string, int offset, int length) + @Override + protected boolean matches(String string, int beginOffset, int endOffset) { - return matcher.matches(string, offset, length); + return matcher.matches(string, beginOffset, endOffset); } } diff --git a/src/proguard/util/StringMatcher.java b/core/src/proguard/util/StringMatcher.java similarity index 74% rename from src/proguard/util/StringMatcher.java rename to core/src/proguard/util/StringMatcher.java index 3e4c3d9a1..f48ed55a0 100644 --- a/src/proguard/util/StringMatcher.java +++ b/core/src/proguard/util/StringMatcher.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,8 +22,8 @@ /** - * This abstract class provides methods to determine whether strings match - * a given criterion, which is specified by the implementation. + * This interface provides methods to determine whether strings match a given + * criterion, which is specified by the implementation. * * @author Eric Lafortune */ @@ -43,9 +43,11 @@ public boolean matches(String string) /** * Checks whether the given substring matches. * @param string the string to match. - * @param offset the start offset of the substring. - * @param length the length of the substring. + * @param beginOffset the start offset of the substring (inclusive). + * @param endOffset the end offset of the substring (exclusive). * @return a boolean indicating whether the substring matches the criterion. */ - protected abstract boolean matches(String string, int offset, int length); + protected abstract boolean matches(String string, + int beginOffset, + int endOffset); } diff --git a/src/proguard/util/StringParser.java b/core/src/proguard/util/StringParser.java similarity index 95% rename from src/proguard/util/StringParser.java rename to core/src/proguard/util/StringParser.java index 1691445ed..ef45c0201 100644 --- a/src/proguard/util/StringParser.java +++ b/core/src/proguard/util/StringParser.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2017 Eric Lafortune @ GuardSquare + * Copyright (c) 2002-2018 GuardSquare NV * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/core/src/proguard/util/StringTransformer.java b/core/src/proguard/util/StringTransformer.java new file mode 100644 index 000000000..a7320a934 --- /dev/null +++ b/core/src/proguard/util/StringTransformer.java @@ -0,0 +1,49 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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; + +/** + * Provides a method for transforming a string into another string. + * + * @author Johan Leys + */ +public interface StringTransformer +{ + /** + * A StringTransformer that doesn't do any transformation. + */ + StringTransformer IDENTITY_TRANSFORMER = new StringTransformer() + { + public String transform(String string) + { + return string; + } + }; + + + /** + * Transforms the given string and returns the transformed result. + * + * @param string a string to be transformed. + * @return the transformed string. + */ + String transform(String string); +} diff --git a/core/src/proguard/util/StringUtil.java b/core/src/proguard/util/StringUtil.java new file mode 100644 index 000000000..932a2fbf4 --- /dev/null +++ b/core/src/proguard/util/StringUtil.java @@ -0,0 +1,203 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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.UnsupportedEncodingException; + +/** + * This class contains utility methods for strings. + */ +public class StringUtil +{ + 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; + + + /** + * Returns the modified UTF-8 byte array representation of the given string. + */ + public static byte[] getUtf8Bytes(String string) + { + // 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. + */ + public static String getString(byte[] bytes) + throws UnsupportedEncodingException + { + return getStringRepresentation(bytes, bytes.length); + } + + + /** + * Returns the String representation of the given modified UTF-8 byte array. + */ + public static String getStringRepresentation(byte[] bytes, int size) + 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 and higher. + + // Allocate the byte array with the computed length. + char[] chars = new char[size]; + + // Fill out the array. + int charIndex = 0; + int byteIndex = 0; + while (byteIndex < size) + { + + 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); + } + + + /** + * Returns the hexadecimal representation of the given byte array. + */ + public static String toHexString(byte[] bytes) + { + return toHexString(bytes, bytes.length); + } + + + /** + * Returns the hexadecimal representation of the given byte array. + */ + public static String toHexString(byte[] bytes, int size) + { + StringBuffer buffer = new StringBuffer(2*size); + + for (int index = 0; index < bytes.length; index++) + { + byte b = bytes[index]; + + buffer.append(hexNibble(b >> 4)).append(hexNibble(b)); + } + + return buffer.toString(); + } + + + /** + * Returns the hexadecimal representation of the given nibble. + */ + private static char hexNibble(int nibble) + { + nibble &= 0xf; + return (char)(nibble < 10 ? + '0' + nibble : + 'a' + nibble - 10); + } +} diff --git a/core/src/proguard/util/VariableStringMatcher.java b/core/src/proguard/util/VariableStringMatcher.java new file mode 100644 index 000000000..8bd5c039a --- /dev/null +++ b/core/src/proguard/util/VariableStringMatcher.java @@ -0,0 +1,268 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2018 GuardSquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * 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 optional given StringMatcher. + * + * @author Eric Lafortune + */ +public class VariableStringMatcher extends StringMatcher +{ + private final char[] allowedCharacters; + private final char[] disallowedCharacters; + private final int minimumLength; + private final int maximumLength; + private final StringMatcher nextMatcher; + + // Remember the most recently attempted match. + private String string; + private int matchingBeginOffset; + private int matchingEndOffset; + private String matchingString; + + + /** + * Creates a new VariableStringMatcher. + * + * @param allowedCharacters an optional list of allowed characters. + * @param disallowedCharacters an optional list of disallowed characters. + * @param minimumLength the minimum length of te variable string. + * @param maximumLength the maximum length of te variable string. + * @param nextMatcher an optional next matcher for the remainder + * of the string. + */ + 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; + } + + + /** + * Returns the string that has been matched most recently. + */ + public String getMatchingString() + { + if (string == null) + { + throw new UnsupportedOperationException("Can't handle regular expression with referenced wildcard"); + } + + // Cache the matching string, since String#substring has become + // expensive since about JRE 1.7. + return matchingString == null ? + (matchingString = string.substring(matchingBeginOffset, matchingEndOffset)) : + matchingString; + } + + + // Implementations for StringMatcher. + + @Override + protected boolean matches(String string, int beginOffset, int endOffset) + { + int length = endOffset - beginOffset; + + // Handle the case without next matcher more efficiently. + if (nextMatcher == null) + { + // Check the length and the characters. + boolean match = + length >= minimumLength && + length <= maximumLength && + areAllowedCharacters(string, beginOffset, endOffset); + + // Does the string match? + if (match) + { + // Remember the matching string, so subsequent external + // matchers can use it. + rememberMatchingString(string, beginOffset, endOffset); + } + else + { + // Reset the matching string, so subsequent external + // matchers cannot use it. + resetMatchingString(); + } + + return match; + } + + // Check the minimum length and the corresponding characters. + if (length < minimumLength || + !areAllowedCharacters(string, beginOffset, beginOffset + minimumLength)) + { + // Reset the matching string, so subsequent external + // matchers cannot use it. + resetMatchingString(); + + return false; + } + + int maximumLength = Math.min(this.maximumLength, length); + + // Check the remaining characters, up to the maximum number. + for (int index = minimumLength; index < maximumLength; index++) + { + int offset = beginOffset + index; + + // Check the next matcher. + if (matchesNextMatcher(string, beginOffset, offset, endOffset)) + { + return true; + } + + // Otherwise just check the next character. + if (!isAllowedCharacter(string.charAt(offset))) + { + // Reset the matching string, so subsequent external + // matchers cannot use it. + resetMatchingString(); + + return false; + } + } + + // Last try: check the remaining characters in the string. + int offset = beginOffset + maximumLength; + + // Check the next matcher. + if (matchesNextMatcher(string, beginOffset, offset, endOffset)) + { + return true; + } + + // Reset the matching string, so subsequent external + // matchers cannot use it. + resetMatchingString(); + + return false; + } + + + // Small utility methods. + + /** + * Returns whether the character characters in the specified substring are + * allowed. + */ + private boolean areAllowedCharacters(String string, int beginOffset, int endOffset) + { + for (int offset = beginOffset; offset < endOffset; offset++) + { + if (!isAllowedCharacter(string.charAt(offset))) + { + return false; + } + } + + return true; + } + + + /** + * 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; + } + + + /** + * Returns whether the next matcher matches the specified second part of + * the string, remembering the first part that matchers this matcher. + */ + private boolean matchesNextMatcher(String string, + int beginOffset, + int splitOffset, + int endOffset) + { + // Remember the matching string, so the next matchers can use it. + rememberMatchingString(string, beginOffset, splitOffset); + + // Check the next matcher. + return nextMatcher.matches(string, splitOffset, endOffset); + } + + + /** + * Remembers the string that is currently being matched. + */ + private void rememberMatchingString(String string, + int matchingBeginOffset, + int matchingEndOffset) + { + this.string = string; + this.matchingBeginOffset = matchingBeginOffset; + this.matchingEndOffset = matchingEndOffset; + this.matchingString = null; + } + + + /** + * Resets the string that is currently being matched. + */ + private void resetMatchingString() + { + this.string = null; + this.matchingBeginOffset = 0; + this.matchingEndOffset = 0; + this.matchingString = null; + } +} diff --git a/src/proguard/util/package.html b/core/src/proguard/util/package.html similarity index 100% rename from src/proguard/util/package.html rename to core/src/proguard/util/package.html diff --git a/docs/FAQ.html b/docs/FAQ.html index 11d1eb621..ec43ae495 100644 --- a/docs/FAQ.html +++ b/docs/FAQ.html @@ -33,7 +33,7 @@

Contents

support?
  • Can I use ProGuard to process my commercial application?
  • -
  • Does ProGuard work with Java 2, 5, ..., 8?
  • +
  • Does ProGuard work with Java 2, 5, ..., 9?
  • Does ProGuard work with Java Micro Edition?
  • Does ProGuard work for Android apps?
  • Does ProGuard work for Blackberry @@ -139,14 +139,15 @@

    Can I use ProGuard to process my commercial appl 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, 5, ..., 8?

    +

    Does ProGuard work with Java 2, 5, ..., 9?

    -Yes, ProGuard supports all JDKs from 1.1 up to and including 8.0. Java +Yes, ProGuard supports all JDKs from 1.1 up to and including 9.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. Java 8 added more attributes and -default methods. ProGuard handles all versions correctly. +default methods. Java 9 added support for modules. ProGuard handles +all versions correctly.

    Does ProGuard work with Java Micro Edition?

    @@ -297,7 +298,7 @@

    How is DexGuard different from ProGuard?
    -Copyright © 2002-2017 +Copyright © 2002-2018 Eric Lafortune @ GuardSquare.
    diff --git a/docs/GPL_exception.html b/docs/GPL_exception.html index 683b2d506..76405668a 100644 --- a/docs/GPL_exception.html +++ b/docs/GPL_exception.html @@ -7,7 +7,7 @@

    Special Exception to the GNU General Public License

    -Copyright © 2002-2017 GuardSquare NV +Copyright © 2002-2018 GuardSquare NV

    diff --git a/docs/acknowledgements.html b/docs/acknowledgements.html index 12678112f..c41f9f6a2 100644 --- a/docs/acknowledgements.html +++ b/docs/acknowledgements.html @@ -51,7 +51,7 @@

    Acknowledgements

    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, Roy Williams, Marcel Heckel, Balazs Banyai, +David Reiss, Roy Williams, Marcel Heckel, Balazs Banyai, Jan Mönnich, and many more. Thanks! Your feedback has been invaluable.

    @@ -81,7 +81,7 @@

    Acknowledgements


    -Copyright © 2002-2017 +Copyright © 2002-2018 Eric Lafortune @ GuardSquare.
    diff --git a/docs/alternatives.html b/docs/alternatives.html index 60163354c..2ca9fd642 100644 --- a/docs/alternatives.html +++ b/docs/alternatives.html @@ -57,7 +57,7 @@

    Alternatives

    -Jochen Hoenicke +Jochen Hoenicke Jode x x @@ -67,37 +67,37 @@

    Alternatives

    -Facebook -ReDex +Google +R8 +x x x
    -
    -Free (BSD) +Free (Apache) -Nate Nystrom -Bloat +Facebook +ReDex x x

    -Free +Free (BSD) -Google -Jack +Nate Nystrom +Bloat x -
    x
    -Free (Apache) +
    +Free -Hidetoshi Ohuchi +Hidetoshi Ohuchi Jarg x
    @@ -107,7 +107,7 @@

    Alternatives

    -yWorks +yWorks yGuard x
    @@ -117,7 +117,7 @@

    Alternatives

    -RiggsHill Software +RiggsHill Software GenJar x
    @@ -127,7 +127,7 @@

    Alternatives

    -Apache +Apache Ant Classfileset x
    @@ -137,7 +137,7 @@

    Alternatives

    -Carsten Elton Sørensen +Carsten Elton Sørensen Treeshaker x
    @@ -147,7 +147,7 @@

    Alternatives

    -Jörg Spieler +Jörg Spieler UCDetector x
    @@ -157,18 +157,8 @@

    Alternatives

    -Romain Guy -Harvester -x -
    -
    -
    -Free (BSD) - - - -Emeric Vernat -DCD +Emeric Vernat +DCD x

    @@ -177,7 +167,7 @@

    Alternatives

    -Cristiano Sadun +Cristiano Sadun Pack x
    @@ -187,7 +177,7 @@

    Alternatives

    -Sable +Sable Soot
    x @@ -197,7 +187,7 @@

    Alternatives

    -Sable +Sable JBCO

    @@ -207,7 +197,7 @@

    Alternatives

    -Jeffrey Wheaton +Jeffrey Wheaton ClassEncrypt

    @@ -217,7 +207,7 @@

    Alternatives

    -Thorsten Heit +Thorsten Heit JavaGuard

    @@ -227,7 +217,7 @@

    Alternatives

    -Patrick Mueller +Patrick Mueller Mwobfu

    @@ -237,7 +227,7 @@

    Alternatives

    -BebboSoft +BebboSoft Bb_mug

    @@ -247,8 +237,8 @@

    Alternatives

    -Vít Šesták -Preverifier +Vít Šesták +Preverifier


    @@ -267,8 +257,8 @@

    Alternatives

    -PreEmptive -DashOPro +PreEmptive +DashOPro x x x @@ -277,7 +267,7 @@

    Alternatives

    -Zelix +Zelix KlassMaster x x @@ -287,7 +277,7 @@

    Alternatives

    -Sophia Cradle +Sophia Cradle SophiaCompress x x @@ -297,7 +287,7 @@

    Alternatives

    -Eastridge Technology +Eastridge Technology Jshrink x
    @@ -307,7 +297,7 @@

    Alternatives

    -Innaworks +Innaworks mBooster x x @@ -317,7 +307,7 @@

    Alternatives

    -Sergey Sverdlov +Sergey Sverdlov J.Class Optimizer x x @@ -327,7 +317,7 @@

    Alternatives

    -Smardec +Smardec Allatori
    x @@ -337,7 +327,7 @@

    Alternatives

    -U. of Arizona +U. of Arizona SandMark
    x @@ -347,7 +337,7 @@

    Alternatives

    -Zenofx +Zenofx ClassGuard

    @@ -357,7 +347,7 @@

    Alternatives

    -BIS Guard & Co. +BIS Guard & Co. Java Antidecompiler

    @@ -367,7 +357,7 @@

    Alternatives

    -Force 5 +Force 5 JCloak

    @@ -377,7 +367,7 @@

    Alternatives

    -Semantic Designs +Semantic Designs Obfuscator

    @@ -387,7 +377,7 @@

    Alternatives

    -Duckware +Duckware Jobfuscate

    @@ -397,8 +387,8 @@

    Alternatives

    -Secureteam -Jfuscator +Secureteam +Jfuscator

    x @@ -407,8 +397,8 @@

    Alternatives

    -Arxan -GuardIT +Arxan +GuardIT

    x @@ -417,7 +407,7 @@

    Alternatives

    -Bfa-it +Bfa-it JarProtector

    @@ -427,7 +417,7 @@

    Alternatives

    -Vasile Calmatui +Vasile Calmatui VasObfuLite

    @@ -437,7 +427,17 @@

    Alternatives

    -IBM AlphaWorks +Google +Jack +x +x +x +
    +(discontinued) + + + +IBM AlphaWorks JAX x x @@ -447,8 +447,8 @@

    Alternatives

    -NQ4 -Joga +NQ4 +Joga x x x @@ -457,8 +457,8 @@

    Alternatives

    -Markus Jansen -Jopt +Markus Jansen +Jopt x x x @@ -467,8 +467,8 @@

    Alternatives

    -Alexander Shvets -CafeBabe +Alexander Shvets +CafeBabe x
    x @@ -477,8 +477,8 @@

    Alternatives

    -Mojo -Minijar +Mojo +Minijar x

    @@ -487,8 +487,18 @@

    Alternatives

    -Brian Alliet -Gcclass +Romain Guy +Harvester +x +
    +
    +
    +disappeared?) + + + +Brian Alliet +Gcclass x

    @@ -497,8 +507,8 @@

    Alternatives

    -Christian Grothoff -Jamit +Christian Grothoff +Jamit x

    @@ -507,8 +517,8 @@

    Alternatives

    -Konstantin Knizhnik -JavaGO +Konstantin Knizhnik +JavaGO
    x
    @@ -517,8 +527,8 @@

    Alternatives

    -Haruaki Tamada -DonQuixote +Haruaki Tamada +DonQuixote
    x x @@ -527,8 +537,8 @@

    Alternatives

    -Bajie -JCMP +Bajie +JCMP
    x x @@ -537,8 +547,8 @@

    Alternatives

    -Elegant Software -JMangle +Elegant Software +JMangle

    x @@ -547,8 +557,8 @@

    Alternatives

    -Eron Jokipii -Jobe +Eron Jokipii +Jobe

    x @@ -557,8 +567,8 @@

    Alternatives

    -JRC -DeCaf +JRC +DeCaf

    x @@ -567,8 +577,8 @@

    Alternatives

    -Dr. Java -Marvin Obfuscator +Dr. Java +Marvin Obfuscator

    x @@ -577,8 +587,8 @@

    Alternatives

    -IBM -WSDD +IBM +WSDD x x x @@ -587,8 +597,8 @@

    Alternatives

    -S5 Systems -jPresto +S5 Systems +jPresto x x x @@ -597,8 +607,8 @@

    Alternatives

    -Plumb Design -Condensity +Plumb Design +Condensity x
    x @@ -607,8 +617,8 @@

    Alternatives

    -4th Pass -SourceGuard +4th Pass +SourceGuard x
    x @@ -617,8 +627,8 @@

    Alternatives

    -CodingArt -CodeShield +CodingArt +CodeShield x
    x @@ -627,8 +637,8 @@

    Alternatives

    -RetroLogic -RetroGuard +RetroLogic +RetroGuard x
    x @@ -637,8 +647,8 @@

    Alternatives

    -Helseth -JObfuscator +Helseth +JObfuscator x
    x @@ -647,8 +657,8 @@

    Alternatives

    -Vega Technologies -JZipper +Vega Technologies +JZipper x
    x @@ -657,8 +667,8 @@

    Alternatives

    -LeeSoftware -Smokescreen Obfuscator +LeeSoftware +Smokescreen Obfuscator x
    x @@ -667,8 +677,8 @@

    Alternatives

    -Eduardo Coca -Shield4J +Eduardo Coca +Shield4J x
    x @@ -677,8 +687,8 @@

    Alternatives

    -Software4j -Obfuscate4j +Software4j +Obfuscate4j

    x @@ -687,8 +697,8 @@

    Alternatives

    -JAMM Consulting -ObfuscatePro +JAMM Consulting +ObfuscatePro

    x @@ -697,8 +707,8 @@

    Alternatives

    -JDevelop -JSCO +JDevelop +JSCO

    x @@ -707,8 +717,8 @@

    Alternatives

    -4Fang -JMix +4Fang +JMix

    x @@ -717,8 +727,8 @@

    Alternatives

    -JProof -JProof +JProof +JProof

    x @@ -727,8 +737,8 @@

    Alternatives

    -ChainKey -Java Code Protector +ChainKey +Java Code Protector

    x @@ -737,8 +747,8 @@

    Alternatives

    -2LKit -2LKit Obfuscator +2LKit +2LKit Obfuscator

    x @@ -747,8 +757,8 @@

    Alternatives

    -WingSoft -WingGuard +WingSoft +WingGuard

    x @@ -757,8 +767,8 @@

    Alternatives

    -HashJava -HashJava +HashJava +HashJava

    x @@ -767,8 +777,8 @@

    Alternatives

    -GITS -Blurfuscator +GITS +Blurfuscator

    x @@ -782,7 +792,7 @@

    Alternatives


    -Copyright © 2002-2017 +Copyright © 2002-2018 Eric Lafortune @ GuardSquare.
    diff --git a/docs/downloads.html b/docs/downloads.html index a6ce1d7c2..ebc4186fa 100644 --- a/docs/downloads.html +++ b/docs/downloads.html @@ -74,8 +74,34 @@

    Downloads

    proguard-retrace. -

    In progress
    Version 5.3.x

    -