From 0ac9591ca1c618ec2b1e7ab5daebb3755e20f247 Mon Sep 17 00:00:00 2001 From: Eric Lafortune Date: Mon, 27 Jan 2020 18:16:51 +0100 Subject: [PATCH] Cleaned up build scripts and documentation. --- base/base.iml | 3 + buildscripts/README | 20 ++-- buildscripts/pom.xml | 54 +++++----- core/build.sh | 6 +- core/docs/md/analyzing.md | 106 ++++++++++++++++++ core/docs/md/building.md | 23 ++++ core/docs/md/creating.md | 33 ++++++ core/docs/md/downloads.md | 18 ++++ core/docs/md/editing.md | 62 +++++++++++ core/docs/md/img/core.png | Bin 0 -> 6100 bytes core/docs/md/img/guardsquare.png | Bin 0 -> 37399 bytes core/docs/md/index.md | 71 ++++++++++++ core/docs/md/license.md | 180 +++++++++++++++++++++++++++++++ core/docs/md/patternmatching.md | 70 ++++++++++++ core/docs/md/reading.md | 84 +++++++++++++++ core/docs/md/releasenotes.md | 5 + core/docs/mkdocs.yml | 70 ++++++++++++ core/functions.sh | 80 ++++++++++++++ core/gradle.properties | 4 +- core/pom.xml | 158 ++++++++++++++++++++++++--- docs/md/manual/retrace/usage.md | 3 +- docs/md/manual/usage.md | 6 +- gui/gui.iml | 3 + retrace/settings.gradle | 2 +- 24 files changed, 999 insertions(+), 62 deletions(-) create mode 100644 core/docs/md/analyzing.md create mode 100644 core/docs/md/building.md create mode 100644 core/docs/md/creating.md create mode 100644 core/docs/md/downloads.md create mode 100644 core/docs/md/editing.md create mode 100644 core/docs/md/img/core.png create mode 100644 core/docs/md/img/guardsquare.png create mode 100644 core/docs/md/index.md create mode 100644 core/docs/md/license.md create mode 100644 core/docs/md/patternmatching.md create mode 100644 core/docs/md/reading.md create mode 100644 core/docs/md/releasenotes.md create mode 100644 core/docs/mkdocs.yml create mode 100755 core/functions.sh diff --git a/base/base.iml b/base/base.iml index 8a5781d6..9602528f 100644 --- a/base/base.iml +++ b/base/base.iml @@ -6,6 +6,9 @@ + + + diff --git a/buildscripts/README b/buildscripts/README index 68999793..8e7e3c36 100644 --- a/buildscripts/README +++ b/buildscripts/README @@ -3,25 +3,17 @@ 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 - - ./build.sh - -- makefile: a makefile for GNU/Linux - - make clean all - - build.gradle : a Gradle build file for all platforms - gradle clean assemble - -- build.xml: an Ant build file for all platforms - - ant clean all + gradle clean assemble - pom.xml: a Maven POM for building the Maven artifacts - mvn clean package + mvn clean package + +- build.sh: a simple and fast shell script for GNU/Linux. + + ./build.sh Pick your favorite build tool and enjoy! diff --git a/buildscripts/pom.xml b/buildscripts/pom.xml index facc09ea..620bf85b 100644 --- a/buildscripts/pom.xml +++ b/buildscripts/pom.xml @@ -40,13 +40,13 @@ - SourceForge.net Tracker - https://sourceforge.net/p/proguard/bugs/ + Github Tracker + https://github.com/Guardsquare/proguard/issues - https://hg.code.sf.net/p/proguard/code - scm:hg:https://hg.code.sf.net/p/proguard/code + https://github.com/Guardsquare/proguard.git + scm:git:https://github.com/Guardsquare/proguard.git @@ -65,6 +65,21 @@ + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + package + + jar + + + + + org.apache.maven.plugins maven-compiler-plugin @@ -74,6 +89,7 @@ 1.8 + org.apache.maven.plugins maven-javadoc-plugin @@ -97,20 +113,6 @@ - - org.apache.maven.plugins - maven-source-plugin - 3.0.1 - - - attach-sources - package - - jar - - - - org.apache.maven.plugins @@ -129,14 +131,6 @@ - - - sonatype-nexus-staging - Nexus Release Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2 - - - ../core ../base @@ -160,4 +154,12 @@ + + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + diff --git a/core/build.sh b/core/build.sh index d860e210..2f7970c6 100755 --- a/core/build.sh +++ b/core/build.sh @@ -4,7 +4,11 @@ cd $(dirname "$0") -source ../buildscripts/functions.sh +if [ -f ../buildscripts/functions.sh ]; then + source ../buildscripts/functions.sh +else + source functions.sh +fi MAIN_CLASS=proguard.* diff --git a/core/docs/md/analyzing.md b/core/docs/md/analyzing.md new file mode 100644 index 00000000..63b82162 --- /dev/null +++ b/core/docs/md/analyzing.md @@ -0,0 +1,106 @@ +## Analyzing all instructions + +If you want to analyze bytecode, you'll probably want to visit specified +instructions of specified code attributes of specified methods of specified +classes. The visitor classes and filters quickly get you to the right place: + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new MyInstructionAnalyzer())))); + +You then only need to implement the visitor methods to analyze the +instructions: + + class MyInstructionAnalyzer + implements InstructionVisitor + { + public void visitSimpleInstruction(Clazz clazz, .....) ... + public void visitVariableInstruction(Clazz clazz, .....) ... + public void visitConstantInstruction(Clazz clazz, .....) ... + public void visitBranchInstruction(Clazz clazz, .....) ... + public void visitTableSwitchInstruction(Clazz clazz, .....) ... + public void visitLookUpSwitchInstruction(Clazz clazz, .....) ... + } + +The library already provides classes to analyze the code for you, finding +branching information, performing partial evaluation, finding the control flow +and data flow, etc, as introduced in the following sections. + +Complete example: EvaluateCode.java + +## Collecting basic branching information + +You can extract basic information about branches in a method with the class +BranchTargetFinder. The results are defined at the instruction level: each +instruction is properly labeled as a branch target, branch origin, exception +handler, etc. + + BranchTargetFinder branchTargetFinder = + new BranchTargetFinder(); + + branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute); + + if (branchTargetFinder.isBranchOrigin(offset)) ... + + if (branchTargetFinder.isBranchTarget(offset)) ... + +Complete example: ApplyPeepholeOptimizations.java + +## Basic control flow analysis + +You can extract a basic control flow graph of the instructions in a method +with partial evaluation (often called abstract evaluation). The core class is +PartialEvaluator. Its results are defined at the instruction level: each +instruction is labeled with potential branch targets and branch origins. + + ValueFactory valueFactory = + new BasicValueFactory(); + + PartialEvaluator partialEvaluator = + new PartialEvaluator( + new BasicInvocationUnit(valueFactory), + false); + + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + InstructionOffsetValue branchOrigins = partialEvaluator.branchOrigins(offset)); + InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset)); + +Complete example: VisualizeControlFlow.java + +## Partial evaluation + +You can extract a lot more information from a method with the same +PartialEvaluator, by tuning the precision of the partial evaluation with +different value factories and different invocation units: + +- A ValueFactory defines the level of detail in representing values like + integers or reference types. The values can be very generic (any primitive + integer, a reference to any object) or more precise (the integer 42, or an + integer between 0 and 5, or a non-null reference to an instance of + java/lang/String). + +- An InvocationUnit defines the values returned from retrieved fields and + invoked methods. The values can again be very generic (any integer) or they + can also be values that were cached in prior evaluations of the code base. + +You can pass them to the PartialEvaluator, apply it to the code, and retrieve +the results: + + ValueFactory valueFactory = + new RangeValueFactory( + new ArrayReferenceValueFactory()); + + PartialEvaluator partialEvaluator = + new PartialEvaluator( + new BasicInvocationUnit(valueFactory), + false); + + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + TracedStack stack = partialEvaluator.getStackAfter(offset); + Value value = stack.getTop(index); + +Complete example: EvaluateCode.java diff --git a/core/docs/md/building.md b/core/docs/md/building.md new file mode 100644 index 00000000..0329e405 --- /dev/null +++ b/core/docs/md/building.md @@ -0,0 +1,23 @@ +If you've downloaded the source code of the **ProGuard Core** library, you can +build it in a number of ways: + +- build.gradle : a Gradle build file for all platforms. + + gradle clean assemble + +- pom.xml: a Maven POM for all platforms. + + mvn clean package + +- build.sh: a simple and fast shell script for GNU/Linux. + + ./build.sh + +Once built, you can include the library and its dependencies in your own +projects. + +You can also build the complete API documentation with + + gradle javadoc + +You can then find the [API documentation](../api/index.html) in `docs/api`. diff --git a/core/docs/md/creating.md b/core/docs/md/creating.md new file mode 100644 index 00000000..035c554e --- /dev/null +++ b/core/docs/md/creating.md @@ -0,0 +1,33 @@ +The easiest way to create a new class from scratch is with ClassBuilder. It +provides a fluent API to add fields and methods. For example, to create a +class that prints out "Hello, world!": + + ProgramClass programClass = + new ClassBuilder( + VersionConstants.CLASS_VERSION_1_8, + AccessConstants.PUBLIC, + "HelloWorld", + ClassConstants.NAME_JAVA_LANG_OBJECT) + + .addMethod( + AccessConstants.PUBLIC | + AccessConstants.STATIC, + "main", + "([Ljava/lang/String;)V", + 50, + + code -> code + .getstatic("java/lang/System", "out", "Ljava/io/PrintStream;") + .ldc("Hello, world!") + .invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V") + .return_()) + + .getProgramClass(); + +You can also use it to add fields and methods to an existing class: + + ProgramClass programClass = + new ClassBuilder(existingClass) + ..... + +Complete example: CreateHelloWorldClass.java diff --git a/core/docs/md/downloads.md b/core/docs/md/downloads.md new file mode 100644 index 00000000..36d8b51c --- /dev/null +++ b/core/docs/md/downloads.md @@ -0,0 +1,18 @@ +The **ProGuard Core** library is distributed under the terms of the Apache +License Version 2.0. Please consult the [license page](license.md) for more +details. + +ProGuard Core is written in Java, so it requires a Java Runtime Environment +(JRE 1.8 or higher). + +You can download the library in various forms: + +- [Pre-built artifacts](https://bintray.com/guardsquare/proguard) at JCenter +- [Pre-built artifacts](https://search.maven.org/search?q=g:net.sf.proguard) at Maven Central +- A [Git repository of the source code](https://github.com/Guardsquare/proguard-core) at Github + +You can find major releases, minor releases with important bug fixes, and +beta releases with the latest new features and any less urgent bug fixes. + +If you're still working with an older version of the library, check out the +[release notes](releasenotes.md), to see if you're missing something essential. diff --git a/core/docs/md/editing.md b/core/docs/md/editing.md new file mode 100644 index 00000000..a5006be1 --- /dev/null +++ b/core/docs/md/editing.md @@ -0,0 +1,62 @@ +## Editing classes + +You can edit existing classes with ClassEditor and related editors like +InterfacesEditor, AttributesEditor, and ConstantPoolEditor. + + ClassEditor classEditor = + new ClassEditor(targetClass); + + classEditor.addField(field); + + classEditor.addMethod(method); + +If you want to create and add new fields or methods from scratch, you can use +the more convenient ClassBuilder: + + ProgramClass programClass = + new ClassBuilder(existingClass) + .addField( + AccessConstants.PUBLIC | + AccessConstants.STATIC, + "someField", + TypeConstants.INT); + + .addMethod( + AccessConstants.PUBLIC | + AccessConstants.STATIC, + "main", + "([Ljava/lang/String;)V", + 50, + + code -> code + .getstatic("java/lang/System", "out", "Ljava/io/PrintStream;") + .ldc("Hello, world!") + .invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V") + .return_()) + + .getProgramClass(); + +## Editing code + +Perhaps more interestingly, you can edit the bytecode of method bodies with +CodeAttributeEditor. + + CodeAttributeEditor codeAttributeEditor = + new CodeAttributeEditor(); + + InstructionSequenceBuilder builder = + new InstructionSequenceBuilder(targetClass); + + Instructions[] replacementInstructions = builder + .getstatic("java/lang/System", "out", "Ljava/io/ + .ldc("Hello") + .invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V" .instructions(); + + // Prepare the editor for this code. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // Insert the instruction sequence before a specified offset. + codeAttributeEditor.insertBeforeOffset(offset, replacementInstructions); + + // Apply the changes. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); diff --git a/core/docs/md/img/core.png b/core/docs/md/img/core.png new file mode 100644 index 0000000000000000000000000000000000000000..1bdc14a4d478777fdfde7665b7918a432a894c32 GIT binary patch literal 6100 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 sI)B_(@?G8)Wi|=_PwjDfPUjz{i3UaArQZvX%Q literal 0 HcmV?d00001 diff --git a/core/docs/md/img/guardsquare.png b/core/docs/md/img/guardsquare.png new file mode 100644 index 0000000000000000000000000000000000000000..0cb2529e4eaf66a241820e34850093db70f679ca GIT binary patch literal 37399 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 H^_Kqy2j7p4 literal 0 HcmV?d00001 diff --git a/core/docs/md/index.md b/core/docs/md/index.md new file mode 100644 index 00000000..a20f67cb --- /dev/null +++ b/core/docs/md/index.md @@ -0,0 +1,71 @@ +**ProGuard Core** is a free library to read, analyze, modify, and write Java +class files. It is the core of the well-known shrinker, optimizer, and +obfuscator [ProGuard](https://www.guardsquare.com/proguard) and of the +[ProGuard Assembler and +Disassembler](https://github.com/guardsquare/proguardassembler). + +Typical applications: + +- Perform peephole optimizations in Java bytecode. +- Search for instruction patterns. +- Analyze code with abstract evaluation. +- Optimize and obfuscate, like ProGuard itself. + +## Design + +The library defines many small classes as the building blocks for applications +that contain the processing logic. This is sometimes taken to the extreme: even +loops and conditional statements can often be implemented as separate classes. +Even though these classes are verbose and repetitive, the resulting main code +becomes much more compact, flexible, and robust. + +### Data classes + +Basic data classes define the structures to represent Java bytecode. They +reflect the Java bytecode specifications literally, to ensure that no data are +lost when reading, analyzing, and writing them. The data classes contain only +a minimum number of methods. They do have one or more accept methods to let +the visitor classes below operate on them. + +### Visitor classes + +The library applies the visitor pattern extensively. Visitor classes define +the operations on the data: reading, writing, editing, transforming, +analyzing, etc. The visitor classes have one or more 'visit' methods to +operate on data classes of the same basic type. + +For example, a Java bytecode class contains a constant pool with constants of +different types: integer constants, float constants, string constants, etc. +The data classes IntegerConstant, FloatConstant, StringConstant, etc. all +implement the basic type Constant. The visitor interface ConstantVisitor +contains methods 'visitIntegerConstant', 'visitFloatConstant', +'visitStringConstant', etc. Implementations of this visitor interface can +perform all kinds of operations on the constants. + +The reasoning behind this pattern is that the data classes are very stable, +because they are directly based on the bytecode specifications. The operations +are more dynamic, since they depend on the final application. It is +practically impossible to add all possible operations in the data classes, but +it is easy to add another implementation of a visitor interface. Implementing +an interface in practice helps a lot to think of all possible cases. + +The visitor pattern uses visitor interfaces to operate on the similar elements +of a data structure. Each interface often has many implementations. A great +disadvantage at this time is that visitor methods can invoke one another +(directly or indirectly), but they can't communicate easily. Since the +implementations can't add their own parameters or return values, they often +have to rely on fields to pass values back and forth. This is more +error-prone. Still, the advantages of the visitor pattern outweigh the +disadvantages. + +### Dependency injection + +The library classes heavily use _constructor-based dependency injection_, to +create immutable instances. Notably the visitor classess are often like +commands that are combined in an immutable structure, via constructors. You +can execute such commands by applying the visitors to the data classes. + +## API + +You can find the complete API in the [ProGuard Core +javadoc](../api/index.html). diff --git a/core/docs/md/license.md b/core/docs/md/license.md new file mode 100644 index 00000000..e65e8242 --- /dev/null +++ b/core/docs/md/license.md @@ -0,0 +1,180 @@ +The ProGuard Core library is licensed under the Apache License Version 2.0. + +Copyright 2002-2020 Guardsquare NV + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/core/docs/md/patternmatching.md b/core/docs/md/patternmatching.md new file mode 100644 index 00000000..d94b25ee --- /dev/null +++ b/core/docs/md/patternmatching.md @@ -0,0 +1,70 @@ +## Basic pattern matching + +The library has powerful support to match patterns in bytecode instruction +sequences. You first define the pattern as a sequence of instructions, with +wildcards. For example: + + final int X = InstructionSequenceMatcher.X; + final int C = InstructionSequenceMatcher.C; + + InstructionSequenceBuilder ____ = + new InstructionSequenceBuilder(); + + Instruction[] pattern = + ____.iload(X) + .bipush(C) + .istore(X).__(); + + Constant[] constants = ____.constants(); + +You can then find that pattern in given code: + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new MyMatchPrinter( + new InstructionSequenceMatcher(constants, pattern)))))); + +Complete example: ApplyPeepholeOptimizations.java + +## Replacing patterns + +Instead of just matching instruction sequences, you can also replace matched +sequences by other instruction sequences, for example to optimize code or +instrument code. The core classes are PeepholeEditor and +InstructionSequenceReplacer. Say that you want to replace an instruction +sequence "putstatic/getstatic" by an equivalent "dup/putstatic": + + final int X = InstructionSequenceReplacer.X; + + InstructionSequenceBuilder ____ = + new InstructionSequenceBuilder(); + + Instruction[][] replacements = + { + ____.putstatic(X) + .getstatic(X).__(), + + ____.dup() + .putstatic(X).__() + }; + + Constant[] constants = ____.constants(); + + BranchTargetFinder branchTargetFinder = new BranchTargetFinder(); + CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new PeepholeEditor(branchTargetFinder, codeAttributeEditor, + new InstructionSequenceReplacer(constants, + replacements, + branchTargetFinder, + codeAttributeEditor))))); + +You can define multiple patterns and their respective replacements in one go, +with the wrapper InstructionSequencesReplacer. + +Complete example: ApplyPeepholeOptimizations.java diff --git a/core/docs/md/reading.md b/core/docs/md/reading.md new file mode 100644 index 00000000..6018dca8 --- /dev/null +++ b/core/docs/md/reading.md @@ -0,0 +1,84 @@ +## Streaming classes from a jar file + +You can read classes from class files and various types of (nested) jar files +or jmod files, with some convenient utility classes and visitors. For example, +you can read the classes from a jar file and print them out in a streaming +fashion, without collecting their representations: + + DirectoryPump directoryPump = + new DirectoryPump( + new File(inputJarFileName)); + + directoryPump.pumpDataEntries( + new JarReader( + new ClassFilter( + new ClassReader(false, false, false, false, null, + new ClassPrinter())))); + +Note the constructor-based dependency injection of visitor classes. We +typically use a slightly unconventional indentation to make this construct +easy to read. + +Complete example: PrintClasses.java + +## Writing out streamed classes + +You can read classes, optionally perform some small modifications, and write +them out right away, again in a streaming fashion. + + JarWriter jarWriter = + new JarWriter( + new ZipWriter( + new FixedFileWriter( + new File(outputJarFileName)))); + + DirectoryPump directoryPump = + new DirectoryPump( + new File(inputJarFileName)); + + directoryPump.pumpDataEntries( + new JarReader( + new ClassFilter( + new ClassReader(false, false, false, false, null, + new DataEntryClassWriter(jarWriter))))); + + jarWriter.close(); + +Complete example: ApplyPeepholeOptimizations.java + +## Collecting classes + +Alternatively, you may want to collect the classes in a so-called class pool +first, so you can perform more extensive analyses on them: + + ClassPool classPool = new ClassPool(); + + DirectoryPump directoryPump = + new DirectoryPump( + new File(jarFileName)); + + directoryPump.pumpDataEntries( + new JarReader(false, + new ClassFilter( + new ClassReader(false, false, false, false, null, + new ClassPoolFiller(classPool))))); + +Complete example: Preverify.java + +## Writing out a set of classes + +If you've collected a set of classes in a class pool, you can write them out +with the same visitors as before. + + JarWriter jarWriter = + new JarWriter( + new ZipWriter( + new FixedFileWriter( + new File(outputJarFileName)))); + + classPool.classesAccept( + new DataEntryClassWriter(jarWriter)); + + jarWriter.close(); + +Complete example: Preverify.java diff --git a/core/docs/md/releasenotes.md b/core/docs/md/releasenotes.md new file mode 100644 index 00000000..f0f4bee5 --- /dev/null +++ b/core/docs/md/releasenotes.md @@ -0,0 +1,5 @@ +## Version 7.0 (Jan 2020) + +| Version| Issue | Module | Explanation +|--------|----------|----------|---------------------------------- +| 7.0.0 | | CORE | Initial release, extracted from ProGuard. diff --git a/core/docs/mkdocs.yml b/core/docs/mkdocs.yml new file mode 100644 index 00000000..ccc46cfc --- /dev/null +++ b/core/docs/mkdocs.yml @@ -0,0 +1,70 @@ +################################################################### +# Project information +################################################################### +site_name: ProGuard Core +site_description: The ProGuard Core library +site_author: Guardsquare NV +copyright: Copyright © 2002-2020 Guardsquare NV + +################################################################### +# Options +################################################################### +theme: + name: null + custom_dir: ../../buildscripts/mkdocs/material + logo: img/core.png + favicon: img/guardsquare.png + language: en + palette: blue + font: + feature: + +docs_dir: md +site_dir: html + +#extra_css: +# - extra.css + +use_directory_urls: false +#strict: true # broken links are errors + +################################################################### +# Theme specific +################################################################### +extra: + #font: + # text: 'Droid Sans' + # code: 'Ubuntu Mono' + social: + - type: 'twitter' + link: 'https://twitter.com/guardsquare' + - type: 'linkedin' + link: 'https://www.linkedin.com/company/guardsquare-nv' + - type: 'facebook' + link: 'https://www.facebook.com/guardsquare' + #feature: + # tabs: true + +################################################################### +# Extensions +################################################################### +markdown_extensions: +- attr_list +- admonition +- footnotes +- def_list + +################################################################### +# Page tree +################################################################### +nav: +- Introduction: index.md +- Reading classes: reading.md +- Creating classes: creating.md +- Editing classes: editing.md +- Pattern matching: patternmatching.md +- Analyzing code: analyzing.md +- License: license.md +- Downloads: downloads.md +- Building: building.md +- Release notes: releasenotes.md diff --git a/core/functions.sh b/core/functions.sh new file mode 100755 index 00000000..593ee57c --- /dev/null +++ b/core/functions.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# +# Support functions for building ProGuard. + +SRC=src +OUT=out +LIB=lib + +TARGET=1.8 + +PROGUARD_JAR=$LIB/proguard.jar + +set -o pipefail + +function download { + if [ ! -f "$2" ]; then + echo "Downloading $2..." + mkdir -p $(dirname "$2") && \ + if type wget > /dev/null 2>&1; then + wget -O "$2" "$1" + else + curl -L -o "$2" "$1" + fi + fi +} + +function compile { + echo "Compiling $(basename $PWD) ($1)..." + mkdir -p "$OUT" && \ + + # Compile Java source files. + find $SRC -name '_*.java' -o -path "$SRC/${1//.//}.java" \ + | xargs --no-run-if-empty \ + javac -nowarn -Xlint:none \ + -source $TARGET -target $TARGET \ + -sourcepath "$SRC" -d "$OUT" \ + ${2:+-classpath "$2"} 2>&1 \ + | sed -e 's|^| |' || return 1 + + # Compile Kotlin source files. + #find $SRC -path "$SRC/${1//.//}.kotlin" \ + #| xargs --no-run-if-empty \ + # kotlinc -nowarn -jvm-target $TARGET \ + # -d "$OUT" \ + # ${2:+-classpath "$2"} 2>&1 \ + #| sed -e 's|^| |' || return 1 + + # Compile Groovy source files. + find $SRC -path "$SRC/${1//.//}.groovy" \ + | xargs --no-run-if-empty \ + groovyc \ + -sourcepath "$SRC" -d "$OUT" \ + ${2:+-classpath "$2"} 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..." + mkdir -p $(dirname "$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..." + if [ -f "$SRC/META-INF/MANIFEST.MF" ]; then + jar -ufm "$1" "$SRC/META-INF/MANIFEST.MF" -C "$OUT" proguard + else + jar -uf "$1" -C "$OUT" proguard + fi +} diff --git a/core/gradle.properties b/core/gradle.properties index 608bcce4..4d5f07ce 100644 --- a/core/gradle.properties +++ b/core/gradle.properties @@ -2,5 +2,5 @@ target = 1.8 -kotlinVersion=1.3.31 -kotlinxMetadataVersion=0.1.0 +kotlinVersion = 1.3.31 +kotlinxMetadataVersion = 0.1.0 diff --git a/core/pom.xml b/core/pom.xml index 1f96ea8b..26687553 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,40 +1,166 @@ - + 4.0.0 - - net.sf.proguard - proguard-parent - 6.2.2 - ../buildscripts/pom.xml - - proguard-base + + net.sf.proguard + proguard-core + 7.0.0 [${project.groupId}] ${project.artifactId} + ProGuard Core is a free library to read, analyze, modify, and write Java class files. + https://github.com/Guardsquare/proguard-core + + + 3 + + + + + lafortune + Eric Lafortune + https://www.guardsquare.com/proguard + Guardsquare + https://www.guardsquare.com/ + + Project Administrator + Developer + + + + + + + Apache License Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + Github Tracker + https://github.com/Guardsquare/proguard-core/issues + + + + https://github.com/Guardsquare/proguard-core.git + scm:git:https://github.com/Guardsquare/proguard-core.git + + + + UTF-8 + src + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + + + org.apache.maven.plugins maven-source-plugin + 3.0.1 + + + attach-sources + package + + jar + + + + + org.apache.maven.plugins maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + https://download.oracle.com/javase/1.5.0/docs/api/ + + true + + + + attach-javadoc + package + + jar + + + -Xdoclint:none + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + prepare-package + + copy-dependencies + + + ${project.build.directory} + false + false + true + + + + + + + org.apache.maven.plugins maven-jar-plugin - proguard.ProGuard + true + - maven-javadoc-plugin + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + @@ -44,25 +170,31 @@ org.jetbrains.kotlin kotlin-stdlib 1.3.31 - provided org.jetbrains.kotlin kotlin-stdlib-common 1.3.31 - provided org.jetbrains.kotlinx kotlinx-metadata-jvm 0.1.0 - provided + jcenter https://jcenter.bintray.com + + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + diff --git a/docs/md/manual/retrace/usage.md b/docs/md/manual/retrace/usage.md index f9742886..2c864198 100644 --- a/docs/md/manual/retrace/usage.md +++ b/docs/md/manual/retrace/usage.md @@ -1,8 +1,7 @@ You can find the ReTrace jar in the `lib` directory of the ProGuard distribution. To run ReTrace, just type: -`java -jar retrace.jar `\[*options...*\] *mapping\_file* -\[*stacktrace\_file*\] + java -jar retrace.jar Alternatively, the `bin` directory contains some short Linux and Windows scripts containing this command. These are the arguments: diff --git a/docs/md/manual/usage.md b/docs/md/manual/usage.md index c015b241..f822eeda 100644 --- a/docs/md/manual/usage.md +++ b/docs/md/manual/usage.md @@ -1,16 +1,16 @@ To run ProGuard, just type: -`bin/proguard `*options* ... + bin/proguard Typically, you'll put most options in a configuration file (say, `myconfig.pro`), and just call: -`bin/proguard @myconfig.pro` + bin/proguard @myconfig.pro You can combine command line options and options from configuration files. For instance: -`bin/proguard @myconfig.pro -verbose` + bin/proguard @myconfig.pro -verbose You can add comments in a configuration file, starting with a `#` character and continuing until the end of the line. diff --git a/gui/gui.iml b/gui/gui.iml index a4838090..ea4eee61 100644 --- a/gui/gui.iml +++ b/gui/gui.iml @@ -6,6 +6,9 @@ + + + diff --git a/retrace/settings.gradle b/retrace/settings.gradle index 429d0260..d003d95c 100644 --- a/retrace/settings.gradle +++ b/retrace/settings.gradle @@ -1,2 +1,2 @@ -includeFlat 'base' +includeFlat 'core' includeFlat 'base'