├── .gitattributes
├── .github
└── workflows
│ └── continuous_integration.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── assembler.iml
├── bin
├── assembler.bat
└── assembler.sh
├── build.gradle
├── docs
├── md
│ ├── building.md
│ ├── downloads.md
│ ├── img
│ │ ├── assembler.png
│ │ └── guardsquare.png
│ ├── index.md
│ ├── license.md
│ ├── releasenotes.md
│ └── specification.md
└── mkdocs.yml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pga-cli
├── build.gradle
└── src
│ └── main
│ └── java
│ └── com
│ └── guardsquare
│ └── proguard
│ └── assembler
│ └── AssemblerCli.java
├── pga-lib
├── build.gradle
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── guardsquare
│ │ └── proguard
│ │ ├── assembler
│ │ ├── AnnotationsParser.java
│ │ ├── AssemblyConstants.java
│ │ ├── AttributesParser.java
│ │ ├── ClassMembersParser.java
│ │ ├── ClassParser.java
│ │ ├── ConstantParser.java
│ │ ├── InstructionsParser.java
│ │ ├── ParseException.java
│ │ ├── Parser.java
│ │ └── io
│ │ │ └── JbcReader.java
│ │ └── disassembler
│ │ ├── AnnotationsPrinter.java
│ │ ├── AttributesPrinter.java
│ │ ├── ClassMembersPrinter.java
│ │ ├── ClassPrinter.java
│ │ ├── ConstantPrinter.java
│ │ ├── InstructionsPrinter.java
│ │ ├── LabelsCollector.java
│ │ ├── PrintException.java
│ │ ├── Printer.java
│ │ └── io
│ │ └── JbcDataEntryWriter.java
│ └── test
│ └── kotlin
│ └── com
│ └── guardsquare
│ └── proguard
│ └── AssemblerErrorsTest.kt
└── settings.gradle
/.gitattributes:
--------------------------------------------------------------------------------
1 | gradlew -text
2 | gradlew.bat -text
3 |
--------------------------------------------------------------------------------
/.github/workflows/continuous_integration.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 | on:
3 | pull_request:
4 | branches:
5 | - master
6 | push:
7 | branches:
8 | - master
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | with:
15 | path: proguard-assembler
16 | - uses: actions/setup-java@v1
17 | with:
18 | java-version: 8
19 | - uses: eskatos/gradle-command-action@v1
20 | with:
21 | build-root-directory: proguard-assembler/
22 | wrapper-directory: proguard-assembler/
23 | arguments: build
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | .gradle/
3 | /lib/
4 | *.iml
5 | .idea
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the ProGuard Assembler and Disassembler
2 |
3 | Thank you for contributing!
4 |
5 | - You can report issues on our [issue tracker](issues) on GitHub. Please be
6 | concise and complete. If we can't reproduce an issue, we most likely can't
7 | fix it.
8 |
9 | - You can also clone the [source code](.) yourself and create [pull
10 | requests](pulls) on GitHub.
11 |
12 | These are a few guidelines for our code and documentation.
13 |
14 | ## Basic principle
15 |
16 | Above all, code should be consistent. Study, respect, and follow the style of
17 | the existing code.
18 |
19 | ## Code design
20 |
21 | The code generally defines many short classes, as the building blocks for
22 | classes that contain the real logic. This is sometimes taken to the extreme:
23 | even loops and conditional statements can often be implemented as separate
24 | classes. Even though these classes are verbose and repetitive, the resulting
25 | main code becomes much more compact, flexible, and robust.
26 |
27 | ### Data classes
28 |
29 | Basic data classes define the data structures, with just a minimum number of
30 | operations: Java bytecode classes, Dex bytecode classes, XML data, native
31 | libraries, etc. They typically reflect their respective specifications
32 | literally. The data classes have one or more `accept` methods to let the
33 | visitor classes below operate on them.
34 |
35 | ### Visitors
36 |
37 | The code relies a lot on the visitor pattern. Visitor classes define the
38 | operations on the data: reading, writing, editing, transforming, etc. The
39 | visitor classes have one or more `visit` methods to operate on data classes of
40 | the same basic type.
41 |
42 | For example, a Java bytecode class contains a constant pool with constants of
43 | different types: integer constants, float constants, string constants, etc.
44 | The data classes IntegerConstant, FloatConstant, StringConstant, etc. all
45 | implement the basic type Constant. The visitor interface ConstantVisitor
46 | contains methods 'visitIntegerConstant', 'visitFloatConstant',
47 | 'visitStringConstant', etc. Implementations of this visitor interface can
48 | perform all kinds of operations on the constants.
49 |
50 | The reasoning behind this pattern is that the data classes are very stable,
51 | because they are directly based on the specifications (class files, dex files,
52 | binary XML files, ELF files, etc). The operations are more dynamic. They are
53 | changed and extended all the time. It is practically impossible to add all
54 | possible operations in the data classes, but it is easy to add another
55 | implementation of a visitor interface. Implementing an interface in practice
56 | helps a lot to think of all possible cases.
57 |
58 | The visitor pattern uses visitor interfaces to operate on the similar elements
59 | of a data structure. Each interface often has many implementations. A great
60 | disadvantage at this time is that visitor methods can invoke one another
61 | (directly or indirectly), but they can't communicate easily. Since the
62 | implementations can't add their own parameters or return values, they often
63 | have to rely on fields to pass values back and forth. This is more
64 | error-prone. Still, the advantages of the visitor pattern outweigh the
65 | disadvantages.
66 |
67 | We prefer (visitor) interfaces with many classes that implement them, so the
68 | implementations can be used as building blocks.
69 |
70 | ### Dependency injection
71 |
72 | We heavily use **constructor-based dependency injection**, to create immutable
73 | objects. Notably the visitors are often like commands that are combined in an
74 | immutable structure, via constructors. The commands are then executed by
75 | applying the visitors to the classes or other data structures.
76 |
77 | ### Getters and setters
78 |
79 | We try to **avoid getters and setters** outside of pure data classes.
80 | Generally, getters and setters break data encapsulation -- "Tell, don't ask".
81 | In our architecture, visitors often delegate to a chain of other visitors,
82 | with the final visitor applying actual changes to the data structures.
83 | Visitors that change the data classes often access fields directly, since they
84 | conceptually have the same purpose as methods inside the data classes.
85 |
86 | ## Code style
87 |
88 | Over time, we've adopted a few guidelines:
89 |
90 | - Historically, ProGuard hasn't used generics, enums, iterable loops, variable
91 | arguments, or closures. More recent code can use the features of Java 8, but
92 | be consistent.
93 |
94 | - Prefer **arrays** over lists, if their sizes are relatively static. The
95 | resulting code is easier to read, without casts or generics.
96 |
97 | - Prefer **short classes** over long classes, although long classes that simply
98 | implement the many methods of their interfaces are okay.
99 |
100 | - Prefer **short methods**, although long methods with a linear flow are okay.
101 |
102 | - **Declare and initialize** fields and variables at the same time, when
103 | possible.
104 |
105 | - Make fields **final**, when possible:
106 | ```
107 | private final CodeAttributeComposer composer = new CodeAttributeComposer();
108 | ```
109 |
110 | - We generally don't make local variables final, to avoid cluttering the code
111 | too much.
112 |
113 | - Maintain a **consistent order** for fields, methods (e.g.. getters and
114 | setters), case statements, etc. A good basic rule for new classes is that
115 | their contents should be in chronological order or respecting the natural
116 | hierarchical order of the data -- Which fields are accessed first? Which
117 | methods are accessed first? Which data comes first in the data structures?
118 | For classes that implement or resemble other classes, the contents should
119 | respect the same order.
120 |
121 | ## Documentation
122 |
123 | All classes must have main javadoc:
124 |
125 | /**
126 | * This {@link ClassVisitor} collects the names of the classes that it
127 | * visits in the given collection.
128 | *
129 | * @author Eric Lafortune
130 | */
131 |
132 | Methods should have javadoc:
133 |
134 | /**
135 | * Returns the number of parameters of the given internal method descriptor.
136 | * @param internalMethodDescriptor the internal method descriptor,
137 | * e.g. "(ID)Z
".
138 | * @param isStatic specifies whether the method is static,
139 | * e.g. false.
140 | * @return the number of parameters,
141 | * e.g. 3.
142 | */
143 |
144 | In practice, fields rarely have javadoc (this is debatable...)
145 |
146 | Methods that implement interfaces generally don't have javadoc, since they
147 | simply implement the behavior specified by the interfaces. Instead, a group of
148 | methods that implements an interface is documented with:
149 |
150 | // Implementations for SomeInterface.
151 |
152 | Most code has comments for each block of statements. The idea is to make the
153 | code readable by just glancing over the comments (which works well if they are
154 | colored in an IDE).
155 |
156 | Comments are generally short sentences, starting with a capital and ending
157 | with a period:
158 |
159 | // This is a comment.
160 |
161 | For the manual (written as markdown), you should follow the excellent advice
162 | of the [Google Developer Documentation Style
163 | Guide](https://developers.google.com/style/).
164 |
165 | ## Code format
166 |
167 | We strive for code that is super-aligned. Ideally, the code should look like a
168 | set of tables, for variable declarations, parameter declarations, ternary
169 | operators, etc.
170 |
171 | - The basic indentation is 4 spaces (never tabs).
172 |
173 | - Curly braces are on separate lines.
174 |
175 | - The ideal maximum line length is 120 characters. Documentation and comments
176 | should stick to this limit. Code that has a nice table-like structure can
177 | exceed it.
178 |
179 | - Imports of multiple classes in the same package are specified with a
180 | wildcard '*'.
181 |
182 | - Try to preserve a logical, consistent order in fields, getters/setters,
183 | methods, variables.
184 |
185 | - Whenever overriding a method, add the @Override annotation.
186 |
187 | In practice, an IDE can help to obtain a clean indentation and structure, but
188 | it is often not sufficient. Be careful to never upset a cleanly formatted
189 | source file by automatically reformatting it.
190 |
191 | ### Naming Conventions
192 |
193 | - Names (class names, field names, etc) should be descriptive.
194 |
195 | - We generally don't abbreviate names, not even index variables:
196 | ```
197 | for (int index = 0; index < count; index++)
198 | ```
199 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Assembler and Disassembler for Java class files
2 |
3 | The **ProGuard Assembler and Disassembler** can assemble and disassemble
4 | Java class files.
5 |
6 | The disassembler takes `class` files and converts them to readable `jbc`
7 | (Java ByteCode) files, following the [ProGuard Assembly Language
8 | specification](docs/md/specification.md).
9 |
10 | The assembler does the opposite; it takes readable `jbc` files and
11 | converts them to `class` files.
12 |
13 | ## Usage
14 |
15 | The program is distributed as a single executable jar file that serves both as
16 | an assembler and as a disassembler. When the program detects a `jbc` file as
17 | input, it assembles it. Vice versa, when it detects a `class` file, it
18 | disassembles it. It can handle any combination of both; a jar file containing
19 | both `jbc` files and `class` files will have its `jbc` files assembled in the
20 | output and its `class` files disassembled in the output.
21 |
22 | bin/assembler []
23 |
24 | The _input_ and the _output_ can be .class/.jbc/.jar/.jmod files or
25 | directories, where _.jbc files_ contain disassembled Java bytecode.
26 |
27 | The _classpath_ (with runtime classes and library classes) is only necessary
28 | for preverifying assembled code.
29 |
30 | ## Example
31 |
32 | As a small test, you can let the program disassemble itself:
33 |
34 | bin/assembler lib/assembler.jar disassembled
35 |
36 | The program will create a `disassembled` directory with readable files
37 | disassembled from the input jar. You can then let the program assemble the
38 | assembly files again:
39 |
40 | bin/assembler /usr/lib/jvm/java-12-oracle/jmods/java.base.jmod disassembled assembled.jar
41 |
42 | The program will now create an `assembled.jar` file with reassembled class
43 | files. It will use the `jmod` file as a run-time library to properly preverify
44 | the code.
45 |
46 | ## Preverification
47 |
48 | The ProGuard Assembly Language contains some syntactic sugar, notably for the
49 | `StackMap` and `StackMapTable` attributes. As of Java version 7, each `Code`
50 | attribute must contain a `StackMapTable` attribute. Since it would be very
51 | cumbersome to write these attributes by hand, the assembler automatically
52 | preverifies every method using the ProGuard preverifier, and creates the
53 | `StackMapTable` using the preverification info. As a consequence, the
54 | assembler needs to know the location of any library jars when assembling
55 | `class` files.
56 |
57 | ## Downloads
58 |
59 | The code is written in Java, so it requires a Java Runtime Environment
60 | (JRE 1.8 or higher).
61 |
62 | You can download the assembler and disassembler in various forms:
63 |
64 | - [Pre-built artifacts](https://search.maven.org/search?q=g:com.guardsquare) at Maven Central
65 | - A [Git repository of the source code](https://github.com/Guardsquare/proguard-assembler) at Github
66 |
67 | ## Building
68 |
69 | If you've downloaded the source code, you can build it with Gradle:
70 |
71 | ./gradlew clean build
72 |
73 | Once built, you can [run the assembler and disassembler](index.md) with the
74 | script `bin/assembler.sh` or `bin/assembler.bat`.
75 |
76 | ## Using as a library
77 |
78 | You can use the ProGuard assembler or disassembler from your own code by
79 | adding a dependency on the ProGuard assembler library. For a more complete
80 | example, see the CLI project (`pga-cli`).
81 |
82 | ```kotlin
83 | import com.guardsquare.proguard.assembler.io.JbcReader
84 | import proguard.classfile.ClassPool
85 | import proguard.classfile.VersionConstants.CLASS_VERSION_1_6
86 | import proguard.classfile.attribute.visitor.AllAttributeVisitor
87 | import proguard.classfile.util.ClassReferenceInitializer
88 | import proguard.classfile.util.ClassSuperHierarchyInitializer
89 | import proguard.classfile.visitor.AllMethodVisitor
90 | import proguard.classfile.visitor.ClassPoolFiller
91 | import proguard.classfile.visitor.ClassVersionFilter
92 | import proguard.io.ClassDataEntryWriter
93 | import proguard.io.DataEntryNameFilter
94 | import proguard.io.DataEntryReader
95 | import proguard.io.DataEntryWriter
96 | import proguard.io.FileSource
97 | import proguard.io.FilteredDataEntryReader
98 | import proguard.io.FixedFileWriter
99 | import proguard.io.IdleRewriter
100 | import proguard.io.RenamedDataEntryWriter
101 | import proguard.io.util.IOUtil
102 | import proguard.preverify.CodePreverifier
103 | import proguard.util.ConcatenatingStringFunction
104 | import proguard.util.ConstantStringFunction
105 | import proguard.util.ExtensionMatcher
106 | import proguard.util.SuffixRemovingStringFunction
107 | import java.io.File
108 |
109 |
110 | fun main(args: Array) {
111 | // Read a JBC file and write a class file
112 |
113 | val inputSource = FileSource(File(args[0]))
114 |
115 | val programClassPool = ClassPool()
116 | val libraryClassPool = if (args.size > 2) IOUtil.read(args[2], true) else ClassPool()
117 |
118 | val jbcReader: DataEntryReader = JbcReader(
119 | ClassPoolFiller(programClassPool)
120 | )
121 |
122 | val reader: DataEntryReader = FilteredDataEntryReader(
123 | DataEntryNameFilter(ExtensionMatcher(".jbc")), jbcReader
124 | )
125 |
126 | inputSource.pumpDataEntries(reader)
127 |
128 | // Preverify before writing the class
129 | preverify(programClassPool, libraryClassPool)
130 |
131 | val outputFile = File(args[1])
132 | val writer = FixedFileWriter(outputFile)
133 |
134 | val jbcAsClassWriter: DataEntryWriter = RenamedDataEntryWriter(
135 | ConcatenatingStringFunction(
136 | SuffixRemovingStringFunction(".jbc"),
137 | ConstantStringFunction(".class")
138 | ),
139 | ClassDataEntryWriter(programClassPool, writer)
140 | )
141 |
142 | // Write the class file by reading in the jbc file and
143 | // replacing it using the `IdleRewriter`.
144 | inputSource.pumpDataEntries(
145 | FilteredDataEntryReader(
146 | DataEntryNameFilter(ExtensionMatcher(".jbc")),
147 | IdleRewriter(jbcAsClassWriter)
148 | )
149 | )
150 |
151 | writer.close()
152 | }
153 |
154 | fun preverify(libraryClassPool: ClassPool, programClassPool: ClassPool) {
155 | programClassPool.classesAccept(ClassReferenceInitializer(programClassPool, libraryClassPool))
156 | programClassPool.classesAccept(ClassSuperHierarchyInitializer(programClassPool, libraryClassPool))
157 | libraryClassPool.classesAccept(ClassReferenceInitializer(programClassPool, libraryClassPool))
158 | libraryClassPool.classesAccept(ClassSuperHierarchyInitializer(programClassPool, libraryClassPool))
159 | programClassPool.classesAccept(
160 | ClassVersionFilter(
161 | CLASS_VERSION_1_6,
162 | AllMethodVisitor(
163 | AllAttributeVisitor(
164 | CodePreverifier(false)
165 | )
166 | )
167 | )
168 | )
169 | }
170 | ```
171 |
172 |
173 | ## Contributing
174 |
175 | The **ProGuard Assembler and Disassembler** are build on the
176 | [ProGuardCORE](https://github.com/Guardsquare/proguard-core) library.
177 |
178 | Contributions, issues and feature requests are welcome in both projects.
179 | Feel free to check the [issues](issues) page and the [contributing
180 | guide](CONTRIBUTING.md) if you would like to contribute.
181 |
182 | ## License
183 |
184 | The **ProGuard Assembler and Disassembler** are distributed under the terms of
185 | the [Apache License Version 2.0](LICENSE).
186 |
187 | Enjoy!
188 |
189 | Copyright (c) 2002-2022 [Guardsquare NV](https://www.guardsquare.com/)
190 |
--------------------------------------------------------------------------------
/assembler.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/bin/assembler.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Start-up script for the ProGuard Assembler and Disassembler.
4 | REM
5 | REM Note: when passing file names containing spaces to this script,
6 | REM you'll have to add escaped quotes around them, e.g.
7 | REM "\"C:/My Directory/My File.txt\""
8 |
9 | IF EXIST "%PROGUARD_HOME%" GOTO home
10 | SET PROGUARD_HOME=%~dp0\..
11 | :home
12 |
13 | java -jar "%PROGUARD_HOME%\lib\assembler.jar" %*
14 |
--------------------------------------------------------------------------------
/bin/assembler.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # Start-up script for the ProGuard Assembler and Disassembler.
4 | #
5 | # Note: when passing file names containing spaces to this script,
6 | # you'll have to add escaped quotes around them, e.g.
7 | # "\"/My Directory/My File.txt\""
8 |
9 | # Account for possibly missing/basic readlink.
10 | # POSIX conformant (dash/ksh/zsh/bash).
11 | ASSEMBLER=`readlink -f "$0" 2>/dev/null`
12 | if test "$ASSEMBLER" = ''
13 | then
14 | ASSEMBLER=`readlink "$0" 2>/dev/null`
15 | if test "$ASSEMBLER" = ''
16 | then
17 | ASSEMBLER="$0"
18 | fi
19 | fi
20 |
21 | ASSEMBLER_HOME=`dirname "$ASSEMBLER"`/..
22 |
23 | # Find the compiled jar.
24 | if [ -f "$ASSEMBLER_HOME/lib/assembler.jar" ]
25 | then
26 | ASSEMBLER_JAR=$ASSEMBLER_HOME/lib/assembler.jar
27 | else
28 | echo 'Please build the project first'
29 | exit 1
30 | fi
31 |
32 | exec java -jar "$ASSEMBLER_JAR" "$@"
33 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | import io.github.gradlenexus.publishplugin.NexusRepository
2 |
3 | plugins {
4 | id 'io.github.gradle-nexus.publish-plugin' version '1.1.0'
5 | id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
6 | id 'com.adarshr.test-logger' version '3.2.0' apply false
7 | id "org.jlleitschuh.gradle.ktlint" version "10.3.0" apply false
8 | id "java-test-fixtures"
9 | }
10 |
11 | allprojects {
12 | group = 'com.guardsquare'
13 | version = '1.0.0'
14 | }
15 |
16 | subprojects {
17 | repositories {
18 | mavenCentral()
19 | mavenLocal()
20 | }
21 | }
22 |
23 | nexusPublishing {
24 | repositories {
25 | sonatype {
26 | username = findProperty('PROGUARD_STAGING_USERNAME') as NexusRepository
27 | password = findProperty('PROGUARD_STAGING_PASSWORD') as NexusRepository
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/docs/md/building.md:
--------------------------------------------------------------------------------
1 | If you've downloaded the source code of the the **ProGuard Assembler and
2 | Disassembler**, you can build it yourself with Gradle:
3 |
4 | - Build the artifacts:
5 |
6 | ./gradlew assemble
7 |
8 | - Build the artifacts when you also have a personal copy of the [ProGuard
9 | Core](https://github.com/Guardsquare/proguard-core) library:
10 |
11 | ./gradlew --include-build assemble
12 |
13 | - Publish the artifacts to your local Maven cache (something like `~/.m2/`):
14 |
15 | ./gradlew publishToMavenLocal
16 |
17 | - Build tar and zip archives with the binaries and documentation:
18 |
19 | ./gradlew distTar distZip
20 |
21 | Once built, you can [run the assembler and disassembler](index.md) with the
22 | script `bin/assembler.sh` or `bin/assembler.bat`.
23 |
--------------------------------------------------------------------------------
/docs/md/downloads.md:
--------------------------------------------------------------------------------
1 | The **ProGuard Assembler and Disassembler** are distributed under the terms of
2 | the Apache License Version 2.0. Please consult the [license page](license.md)
3 | for more details.
4 |
5 | The code is written in Java, so it requires a Java Runtime Environment
6 | (JRE 1.8 or higher).
7 |
8 | You can download the assembler and disassembler in various forms:
9 |
10 | - [Pre-built artifacts](https://bintray.com/guardsquare/proguard) at JCenter
11 | - [Pre-built artifacts](https://search.maven.org/search?q=g:net.sf.proguard) at Maven Central
12 | - A [Git repository of the source code](https://github.com/Guardsquare/proguard-assembler) at Github
13 |
14 | You can find major releases, minor releases with important bug fixes, and
15 | beta releases with the latest new features and any less urgent bug fixes.
16 |
17 | If you're still working with an older version of the software, check out the
18 | [release notes](releasenotes.md), to see if you're missing something essential.
19 |
--------------------------------------------------------------------------------
/docs/md/img/assembler.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Guardsquare/proguard-assembler/a923540f0a0b2cae6acb52c3a4e50eff45129abf/docs/md/img/assembler.png
--------------------------------------------------------------------------------
/docs/md/img/guardsquare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Guardsquare/proguard-assembler/a923540f0a0b2cae6acb52c3a4e50eff45129abf/docs/md/img/guardsquare.png
--------------------------------------------------------------------------------
/docs/md/index.md:
--------------------------------------------------------------------------------
1 | The **ProGuard Assembler and Disassembler** can assemble and disassemble
2 | Java class files.
3 |
4 | The disassembler takes `class` files and converts them to readable `jbc`
5 | (Java ByteCode) files, following the [ProGuard Assembly Language
6 | specification](specification.md).
7 |
8 | The assembler does the opposite; it takes readable `jbc` files and
9 | converts them to `class` files.
10 |
11 | # Usage
12 |
13 | The program is distributed as a single executable jar file that serves both as
14 | an assembler and as a disassembler. When the program detects a `jbc` file as
15 | input, it assembles it. Vice versa, when it detects a `class` file, it
16 | disassembles it. It can handle a combination of both; a jar file containing
17 | both `jbc` files and `class` files will have its `jbc` files assembled in the
18 | output and its `class` files disassembled in the output.
19 |
20 | bin/assembler []
21 |
22 | The _input_ and the _output_ can be .class/.jbc/.jar/.jmod files or
23 | directories, where _.jbc files_ contain disassembled Java bytecode.
24 |
25 | The _classpath_ (with runtime classes and library classes) is only necessary
26 | for preverifying assembled code.
27 |
28 | # Example
29 |
30 | As a small test, you can let the program disassemble itself:
31 |
32 | bin/assembler lib/assembler.jar disassembled
33 |
34 | The program will create a `disassembled` directory with readable files
35 | disassembled from the input jar. You can then let the program assemble the
36 | assembly files again:
37 |
38 | bin/assembler /usr/lib/jvm/java-12-oracle/jmods/java.base.jmod disassembled assembled.jar
39 |
40 | The program will now create an `assembled.jar` file with reassembled class
41 | files. It will use the `jmod` file as a run-time library to properly preverify
42 | the code.
43 |
44 | # Preverification
45 |
46 | The ProGuard Assembly Language contains some syntactic sugar, notably for the
47 | `StackMap` and `StackMapTable` attributes. As of Java version 7, each `Code`
48 | attribute must contain a `StackMapTable` attribute. Since it would be very
49 | cumbersome to write these attributes by hand, the assembler automatically
50 | preverifies every method using the ProGuard preverifier, and creates the
51 | `StackMapTable` using the preverification info. As a consequence, the
52 | assembler needs to know the location of any library jars when assembling
53 | `class` files.
54 |
--------------------------------------------------------------------------------
/docs/md/license.md:
--------------------------------------------------------------------------------
1 | The ProGuard Assembler and Disassembler are licensed under the Apache License
2 | Version 2.0.
3 |
4 | Copyright 2002-2020 Guardsquare NV
5 |
6 | Apache License
7 | Version 2.0, January 2004
8 | http://www.apache.org/licenses/
9 |
10 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
11 |
12 | 1. Definitions.
13 |
14 | "License" shall mean the terms and conditions for use, reproduction,
15 | and distribution as defined by Sections 1 through 9 of this document.
16 |
17 | "Licensor" shall mean the copyright owner or entity authorized by
18 | the copyright owner that is granting the License.
19 |
20 | "Legal Entity" shall mean the union of the acting entity and all
21 | other entities that control, are controlled by, or are under common
22 | control with that entity. For the purposes of this definition,
23 | "control" means (i) the power, direct or indirect, to cause the
24 | direction or management of such entity, whether by contract or
25 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
26 | outstanding shares, or (iii) beneficial ownership of such entity.
27 |
28 | "You" (or "Your") shall mean an individual or Legal Entity
29 | exercising permissions granted by this License.
30 |
31 | "Source" form shall mean the preferred form for making modifications,
32 | including but not limited to software source code, documentation
33 | source, and configuration files.
34 |
35 | "Object" form shall mean any form resulting from mechanical
36 | transformation or translation of a Source form, including but
37 | not limited to compiled object code, generated documentation,
38 | and conversions to other media types.
39 |
40 | "Work" shall mean the work of authorship, whether in Source or
41 | Object form, made available under the License, as indicated by a
42 | copyright notice that is included in or attached to the work
43 | (an example is provided in the Appendix below).
44 |
45 | "Derivative Works" shall mean any work, whether in Source or Object
46 | form, that is based on (or derived from) the Work and for which the
47 | editorial revisions, annotations, elaborations, or other modifications
48 | represent, as a whole, an original work of authorship. For the purposes
49 | of this License, Derivative Works shall not include works that remain
50 | separable from, or merely link (or bind by name) to the interfaces of,
51 | the Work and Derivative Works thereof.
52 |
53 | "Contribution" shall mean any work of authorship, including
54 | the original version of the Work and any modifications or additions
55 | to that Work or Derivative Works thereof, that is intentionally
56 | submitted to Licensor for inclusion in the Work by the copyright owner
57 | or by an individual or Legal Entity authorized to submit on behalf of
58 | the copyright owner. For the purposes of this definition, "submitted"
59 | means any form of electronic, verbal, or written communication sent
60 | to the Licensor or its representatives, including but not limited to
61 | communication on electronic mailing lists, source code control systems,
62 | and issue tracking systems that are managed by, or on behalf of, the
63 | Licensor for the purpose of discussing and improving the Work, but
64 | excluding communication that is conspicuously marked or otherwise
65 | designated in writing by the copyright owner as "Not a Contribution."
66 |
67 | "Contributor" shall mean Licensor and any individual or Legal Entity
68 | on behalf of whom a Contribution has been received by Licensor and
69 | subsequently incorporated within the Work.
70 |
71 | 2. Grant of Copyright License. Subject to the terms and conditions of
72 | this License, each Contributor hereby grants to You a perpetual,
73 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
74 | copyright license to reproduce, prepare Derivative Works of,
75 | publicly display, publicly perform, sublicense, and distribute the
76 | Work and such Derivative Works in Source or Object form.
77 |
78 | 3. Grant of Patent License. Subject to the terms and conditions of
79 | this License, each Contributor hereby grants to You a perpetual,
80 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
81 | (except as stated in this section) patent license to make, have made,
82 | use, offer to sell, sell, import, and otherwise transfer the Work,
83 | where such license applies only to those patent claims licensable
84 | by such Contributor that are necessarily infringed by their
85 | Contribution(s) alone or by combination of their Contribution(s)
86 | with the Work to which such Contribution(s) was submitted. If You
87 | institute patent litigation against any entity (including a
88 | cross-claim or counterclaim in a lawsuit) alleging that the Work
89 | or a Contribution incorporated within the Work constitutes direct
90 | or contributory patent infringement, then any patent licenses
91 | granted to You under this License for that Work shall terminate
92 | as of the date such litigation is filed.
93 |
94 | 4. Redistribution. You may reproduce and distribute copies of the
95 | Work or Derivative Works thereof in any medium, with or without
96 | modifications, and in Source or Object form, provided that You
97 | meet the following conditions:
98 |
99 | (a) You must give any other recipients of the Work or
100 | Derivative Works a copy of this License; and
101 |
102 | (b) You must cause any modified files to carry prominent notices
103 | stating that You changed the files; and
104 |
105 | (c) You must retain, in the Source form of any Derivative Works
106 | that You distribute, all copyright, patent, trademark, and
107 | attribution notices from the Source form of the Work,
108 | excluding those notices that do not pertain to any part of
109 | the Derivative Works; and
110 |
111 | (d) If the Work includes a "NOTICE" text file as part of its
112 | distribution, then any Derivative Works that You distribute must
113 | include a readable copy of the attribution notices contained
114 | within such NOTICE file, excluding those notices that do not
115 | pertain to any part of the Derivative Works, in at least one
116 | of the following places: within a NOTICE text file distributed
117 | as part of the Derivative Works; within the Source form or
118 | documentation, if provided along with the Derivative Works; or,
119 | within a display generated by the Derivative Works, if and
120 | wherever such third-party notices normally appear. The contents
121 | of the NOTICE file are for informational purposes only and
122 | do not modify the License. You may add Your own attribution
123 | notices within Derivative Works that You distribute, alongside
124 | or as an addendum to the NOTICE text from the Work, provided
125 | that such additional attribution notices cannot be construed
126 | as modifying the License.
127 |
128 | You may add Your own copyright statement to Your modifications and
129 | may provide additional or different license terms and conditions
130 | for use, reproduction, or distribution of Your modifications, or
131 | for any such Derivative Works as a whole, provided Your use,
132 | reproduction, and distribution of the Work otherwise complies with
133 | the conditions stated in this License.
134 |
135 | 5. Submission of Contributions. Unless You explicitly state otherwise,
136 | any Contribution intentionally submitted for inclusion in the Work
137 | by You to the Licensor shall be under the terms and conditions of
138 | this License, without any additional terms or conditions.
139 | Notwithstanding the above, nothing herein shall supersede or modify
140 | the terms of any separate license agreement you may have executed
141 | with Licensor regarding such Contributions.
142 |
143 | 6. Trademarks. This License does not grant permission to use the trade
144 | names, trademarks, service marks, or product names of the Licensor,
145 | except as required for reasonable and customary use in describing the
146 | origin of the Work and reproducing the content of the NOTICE file.
147 |
148 | 7. Disclaimer of Warranty. Unless required by applicable law or
149 | agreed to in writing, Licensor provides the Work (and each
150 | Contributor provides its Contributions) on an "AS IS" BASIS,
151 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
152 | implied, including, without limitation, any warranties or conditions
153 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
154 | PARTICULAR PURPOSE. You are solely responsible for determining the
155 | appropriateness of using or redistributing the Work and assume any
156 | risks associated with Your exercise of permissions under this License.
157 |
158 | 8. Limitation of Liability. In no event and under no legal theory,
159 | whether in tort (including negligence), contract, or otherwise,
160 | unless required by applicable law (such as deliberate and grossly
161 | negligent acts) or agreed to in writing, shall any Contributor be
162 | liable to You for damages, including any direct, indirect, special,
163 | incidental, or consequential damages of any character arising as a
164 | result of this License or out of the use or inability to use the
165 | Work (including but not limited to damages for loss of goodwill,
166 | work stoppage, computer failure or malfunction, or any and all
167 | other commercial damages or losses), even if such Contributor
168 | has been advised of the possibility of such damages.
169 |
170 | 9. Accepting Warranty or Additional Liability. While redistributing
171 | the Work or Derivative Works thereof, You may choose to offer,
172 | and charge a fee for, acceptance of support, warranty, indemnity,
173 | or other liability obligations and/or rights consistent with this
174 | License. However, in accepting such obligations, You may act only
175 | on Your own behalf and on Your sole responsibility, not on behalf
176 | of any other Contributor, and only if You agree to indemnify,
177 | defend, and hold each Contributor harmless for any liability
178 | incurred by, or claims asserted against, such Contributor by reason
179 | of your accepting any such warranty or additional liability.
180 |
181 | END OF TERMS AND CONDITIONS
182 |
--------------------------------------------------------------------------------
/docs/md/releasenotes.md:
--------------------------------------------------------------------------------
1 | ## Version 1.0 (Jan 2020)
2 |
3 | | Version| Issue | Module | Explanation
4 | |--------|----------|----------|----------------------------------
5 | | 1.0.0 | | ASSEM | Initial release.
6 |
--------------------------------------------------------------------------------
/docs/mkdocs.yml:
--------------------------------------------------------------------------------
1 | ###################################################################
2 | # Project information
3 | ###################################################################
4 | site_name: ProGuard Assembler and Disassembler
5 | site_description: An assembler and disassembler for Java bytecode.
6 | site_author: Guardsquare NV
7 | copyright: Copyright © 2002-2020 Guardsquare NV
8 |
9 | ###################################################################
10 | # Options
11 | ###################################################################
12 | theme:
13 | name: null
14 | custom_dir: ../../proguard_lib/buildscripts/mkdocs/material
15 | logo: img/assembler.png
16 | favicon: img/guardsquare.png
17 | language: en
18 | palette: blue
19 | font:
20 | feature:
21 |
22 | docs_dir: md
23 | site_dir: html
24 |
25 | #extra_css:
26 | # - extra.css
27 |
28 | use_directory_urls: false
29 | #strict: true # broken links are errors
30 |
31 | ###################################################################
32 | # Theme specific
33 | ###################################################################
34 | extra:
35 | #font:
36 | # text: 'Droid Sans'
37 | # code: 'Ubuntu Mono'
38 | social:
39 | - type: 'twitter'
40 | link: 'https://twitter.com/guardsquare'
41 | - type: 'linkedin'
42 | link: 'https://www.linkedin.com/company/guardsquare-nv'
43 | - type: 'facebook'
44 | link: 'https://www.facebook.com/guardsquare'
45 | #feature:
46 | # tabs: true
47 |
48 | ###################################################################
49 | # Extensions
50 | ###################################################################
51 | markdown_extensions:
52 | - attr_list
53 | - admonition
54 | - footnotes
55 | - def_list
56 |
57 | ###################################################################
58 | # Page tree
59 | ###################################################################
60 | nav:
61 | - Introduction: index.md
62 | - Specification: specification.md
63 | - License: license.md
64 | - Downloads: downloads.md
65 | - Building: building.md
66 | - Release notes: releasenotes.md
67 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Gradle build properties for the ProGuard Assembler and Disassembler.
2 |
3 | target = 1.8
4 |
5 | kotlinVersion = 1.9.0
6 | kotestVersion = 5.6.2
7 | proguardCoreVersion = 9.1.4
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Guardsquare/proguard-assembler/a923540f0a0b2cae6acb52c3a4e50eff45129abf/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo. 1>&2
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
48 | echo. 1>&2
49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
50 | echo location of your Java installation. 1>&2
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo. 1>&2
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
62 | echo. 1>&2
63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
64 | echo location of your Java installation. 1>&2
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/pga-cli/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'distribution'
3 | id 'java'
4 | id 'application'
5 | id 'maven-publish'
6 | id 'signing'
7 | }
8 |
9 | java {
10 | toolchain {
11 | languageVersion.set(JavaLanguageVersion.of(8))
12 | }
13 | }
14 |
15 | dependencies {
16 | implementation project(":pga-lib")
17 | }
18 |
19 | application {
20 | mainClass = 'com.guardsquare.proguard.assembler.AssemblerCli'
21 | }
22 |
23 | jar {
24 | dependsOn(":pga-lib:jar")
25 | manifest {
26 | attributes 'Main-Class': application.mainClass
27 | }
28 | from {
29 | configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
30 | }
31 | archiveFileName = "assembler.jar"
32 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE
33 | }
34 |
35 | tasks.register('copyBuild', Copy) {
36 | dependsOn tasks.jar
37 |
38 | from tasks.jar.outputs
39 | into file("$rootDir/lib")
40 | }
41 |
42 | tasks.named("build") { finalizedBy("copyBuild") }
43 |
44 | distributions {
45 | main {
46 | distributionBaseName.set('proguard-assembler')
47 | contents {
48 | into("$rootDir/lib") {
49 | from jar.outputs
50 | }
51 | into("docs") {
52 | from("$rootDir/docs/md") {
53 | includeEmptyDirs = false
54 | include '**/*.md'
55 | }
56 | }
57 | from(rootDir) {
58 | include "$rootDir/bin/"
59 | include 'LICENSE'
60 | }
61 | }
62 | }
63 | }
64 |
65 | distTar {
66 | compression = Compression.GZIP
67 | archiveExtension.set('tar.gz')
68 | }
69 |
70 | clean {
71 | delete file("$rootDir/lib")
72 | }
73 |
--------------------------------------------------------------------------------
/pga-cli/src/main/java/com/guardsquare/proguard/assembler/AssemblerCli.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.assembler;
19 |
20 | import com.guardsquare.proguard.disassembler.io.JbcDataEntryWriter;
21 | import com.guardsquare.proguard.assembler.io.JbcReader;
22 | import proguard.classfile.*;
23 | import proguard.classfile.attribute.visitor.AllAttributeVisitor;
24 | import proguard.classfile.editor.ConstantPoolSorter;
25 | import proguard.classfile.util.*;
26 | import proguard.classfile.visitor.*;
27 | import proguard.io.*;
28 | import proguard.preverify.CodePreverifier;
29 | import proguard.util.*;
30 |
31 | import java.io.*;
32 |
33 | /**
34 | * Main class for the ProGuard Disassembler and Assembler.
35 | *
36 | * @author Joachim Vandersmissen
37 | */
38 | public class AssemblerCli
39 | {
40 | private static final byte[] JMOD_HEADER = new byte[] { 'J', 'M', 1, 0 };
41 | private static final String JMOD_CLASS_FILE_PREFIX = "classes/";
42 |
43 |
44 | public static void main(String[] args) throws IOException
45 | {
46 | if (args.length < 2 || args.length > 3)
47 | {
48 | System.out.println("ProGuard Assembler: assembles and disassembles Java class files.");
49 | System.out.println("Usage:");
50 | System.out.println(" java proguard.Assembler [] ");
51 | System.out.println("The input and the output can be .class/.jbc/.jar/.jmod files or directories,");
52 | System.out.println("where .jbc files contain disassembled Java bytecode.");
53 | System.out.println("The classpath (with runtime classes and library classes) is only necessary for preverifying assembled code.");
54 | System.exit(1);
55 | }
56 |
57 | int index = 0;
58 |
59 | String[] libraryPath = args.length >= 3 ?
60 | args[index++].split(System.getProperty("path.separator")) : null;
61 | String inputFileName = args[index++];
62 | String outputFileName = args[index++];
63 |
64 | ClassPool libraryClassPool = new ClassPool();
65 | ClassPool programClassPool = new ClassPool();
66 |
67 | // Read the libraries.
68 | if (libraryPath != null)
69 | {
70 | readLibraries(libraryPath, libraryClassPool);
71 | }
72 |
73 | // Read the actual input.
74 | DataEntrySource inputSource =
75 | source(inputFileName);
76 |
77 | System.out.println("Reading input file ["+inputFileName+"]...");
78 |
79 | readInput(inputSource,
80 | libraryClassPool,
81 | programClassPool);
82 |
83 | // Preverify the program class pool.
84 | if (libraryPath != null && programClassPool.size() > 0)
85 | {
86 | System.out.println("Preverifying assembled class files...");
87 |
88 | preverify(libraryClassPool,
89 | programClassPool);
90 | }
91 |
92 | // Clean up the program classes.
93 | programClassPool.classesAccept(
94 | new ConstantPoolSorter());
95 |
96 | // Write out the output.
97 | System.out.println("Writing output file ["+outputFileName+"]...");
98 |
99 | writeOutput(inputSource,
100 | outputFileName,
101 | libraryClassPool,
102 | programClassPool);
103 | }
104 |
105 |
106 | /**
107 | * Reads the specified libraries as library classes in the given class pool.
108 | */
109 | private static void readLibraries(String[] libraryPath,
110 | ClassPool libraryClassPool)
111 | throws IOException
112 | {
113 | for (String libraryFileName : libraryPath)
114 | {
115 | System.out.println("Reading library file ["+libraryFileName+"]...");
116 |
117 | // Any libraries go into the library class pool.
118 | DataEntryReader libraryClassReader =
119 | new ClassReader(true, false, false, false, null,
120 | new ClassPoolFiller(libraryClassPool));
121 |
122 | DataEntrySource librarySource =
123 | source(libraryFileName);
124 |
125 | librarySource.pumpDataEntries(reader(libraryClassReader,
126 | null,
127 | null));
128 | }
129 | }
130 |
131 |
132 | /**
133 | * Reads the specified input as program classes (for .jbc files) and
134 | * library classes (for .class files) in the given class pools.
135 | */
136 | private static void readInput(DataEntrySource inputSource,
137 | ClassPool libraryClassPool,
138 | ClassPool programClassPool)
139 | throws IOException
140 | {
141 | // Any class files go into the library class pool.
142 | DataEntryReader classReader =
143 | new ClassReader(false, false, false, false, null,
144 | new ClassPoolFiller(libraryClassPool));
145 |
146 | // Any jbc files go into the program class pool.
147 | DataEntryReader jbcReader =
148 | new JbcReader(
149 | new ClassPoolFiller(programClassPool));
150 |
151 | DataEntryReader reader =
152 | reader(classReader,
153 | jbcReader,
154 | null);
155 |
156 | inputSource.pumpDataEntries(reader);
157 | }
158 |
159 |
160 | /**
161 | * Preverifies the program classes in the given class pools.
162 | */
163 | private static void preverify(ClassPool libraryClassPool,
164 | ClassPool programClassPool)
165 | {
166 | programClassPool.classesAccept(new ClassReferenceInitializer(programClassPool, libraryClassPool));
167 | programClassPool.classesAccept(new ClassSuperHierarchyInitializer(programClassPool, libraryClassPool));
168 | libraryClassPool.classesAccept(new ClassReferenceInitializer(programClassPool, libraryClassPool));
169 | libraryClassPool.classesAccept(new ClassSuperHierarchyInitializer(programClassPool, libraryClassPool));
170 |
171 | programClassPool.classesAccept(
172 | new ClassVersionFilter(VersionConstants.CLASS_VERSION_1_6,
173 | new AllMethodVisitor(
174 | new AllAttributeVisitor(
175 | new CodePreverifier(false)))));
176 | }
177 |
178 |
179 | /**
180 | * Reads the specified input, replacing .class files by .jbc files and
181 | * vice versa, and writing them to the specified output.
182 | */
183 | private static void writeOutput(DataEntrySource inputSource,
184 | String outputFileName,
185 | ClassPool libraryClassPool,
186 | ClassPool programClassPool)
187 | throws IOException
188 | {
189 | DataEntryWriter writer = writer(outputFileName);
190 |
191 | // Write out class files as jbc files.
192 | DataEntryWriter classAsJbcWriter =
193 | new RenamedDataEntryWriter(new ConcatenatingStringFunction(
194 | new SuffixRemovingStringFunction(".class"),
195 | new ConstantStringFunction(".jbc")),
196 | new JbcDataEntryWriter(libraryClassPool, writer));
197 |
198 | // Write out jbc files as class files.
199 | DataEntryWriter jbcAsClassWriter =
200 | new RenamedDataEntryWriter(new ConcatenatingStringFunction(
201 | new SuffixRemovingStringFunction(".jbc"),
202 | new ConstantStringFunction(".class")),
203 | new ClassDataEntryWriter(programClassPool, writer));
204 |
205 | // Read the input again, writing out disassembled/assembled/preverified
206 | // files.
207 | inputSource.pumpDataEntries(reader(new IdleRewriter(classAsJbcWriter),
208 | new IdleRewriter(jbcAsClassWriter),
209 | new DataEntryCopier(writer)));
210 |
211 | writer.close();
212 | }
213 |
214 |
215 | /**
216 | * Creates a data entry source for the given file name.
217 | */
218 | private static DataEntrySource source(String inputFileName)
219 | {
220 | boolean isJar = inputFileName.endsWith(".jar");
221 | boolean isJmod = inputFileName.endsWith(".jmod");
222 | boolean isJbc = inputFileName.endsWith(".jbc");
223 | boolean isClass = inputFileName.endsWith(".class");
224 |
225 | File inputFile = new File(inputFileName);
226 |
227 | return isJar ||
228 | isJmod ||
229 | isJbc ||
230 | isClass ?
231 | new FileSource(inputFile) :
232 | new DirectorySource(inputFile);
233 | }
234 |
235 |
236 | /**
237 | * Creates a data entry reader that unpacks archives if necessary and sends
238 | * data entries to given relevant readers.
239 | */
240 | private static DataEntryReader reader(DataEntryReader classReader,
241 | DataEntryReader jbcReader,
242 | DataEntryReader resourceReader)
243 | {
244 | // Handle class files and resource files.
245 | DataEntryReader reader =
246 | new FilteredDataEntryReader(new DataEntryNameFilter(new ExtensionMatcher(".class")),
247 | classReader,
248 | resourceReader);
249 |
250 | // Handle jbc files.
251 | reader =
252 | new FilteredDataEntryReader(new DataEntryNameFilter(new ExtensionMatcher(".jbc")),
253 | jbcReader,
254 | reader);
255 |
256 | // Strip the "classes/" prefix from class/jbc files inside jmod files.
257 | DataEntryReader prefixStrippingReader =
258 | new FilteredDataEntryReader(new DataEntryNameFilter(
259 | new OrMatcher(
260 | new ExtensionMatcher(".class"),
261 | new ExtensionMatcher(".jbc"))),
262 | new PrefixStrippingDataEntryReader(JMOD_CLASS_FILE_PREFIX, reader),
263 | reader);
264 |
265 | // Unpack jmod files.
266 | reader =
267 | new FilteredDataEntryReader(new DataEntryNameFilter(new ExtensionMatcher(".jmod")),
268 | new JarReader(true, prefixStrippingReader),
269 | reader);
270 |
271 | // Unpack jar files.
272 | reader =
273 | new FilteredDataEntryReader(new DataEntryNameFilter(new ExtensionMatcher(".jar")),
274 | new JarReader(reader),
275 | reader);
276 |
277 | return reader;
278 | }
279 |
280 |
281 | /**
282 | * Creates a data entry writer for the given file name that creates archives
283 | * if necessary.
284 | */
285 | private static DataEntryWriter writer(String outputFileName)
286 | {
287 | boolean isJar = outputFileName.endsWith(".jar");
288 | boolean isJmod = outputFileName.endsWith(".jmod");
289 | boolean isJbc = outputFileName.endsWith(".jbc");
290 | boolean isClass = outputFileName.endsWith(".class");
291 |
292 | File outputFile = new File(outputFileName);
293 |
294 | DataEntryWriter writer =
295 | isJar ||
296 | isJmod ||
297 | isJbc ||
298 | isClass ?
299 | new FixedFileWriter(outputFile) :
300 | new DirectoryWriter(outputFile);
301 |
302 | // Pack jar files.
303 | if (isJar)
304 | {
305 | writer =
306 | new JarWriter(
307 | new ZipWriter(writer));
308 | }
309 |
310 | // Pack jmod files.
311 | else if (isJmod)
312 | {
313 | writer =
314 | new JarWriter(
315 | new ZipWriter(null, 1, false, 0, JMOD_HEADER,
316 | writer));
317 |
318 | // Add the "classes/" prefix to class/jbc files inside jmod files.
319 | writer =
320 | new FilteredDataEntryWriter(new DataEntryNameFilter(
321 | new OrMatcher(
322 | new ExtensionMatcher(".class"),
323 | new ExtensionMatcher(".jbc"))),
324 | new PrefixAddingDataEntryWriter(JMOD_CLASS_FILE_PREFIX, writer),
325 | writer);
326 | }
327 |
328 | return writer;
329 | }
330 | }
331 |
--------------------------------------------------------------------------------
/pga-lib/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'java-library'
4 | id 'maven-publish'
5 | id 'signing'
6 |
7 | id "org.jetbrains.kotlin.jvm" version "$kotlinVersion"
8 | id 'com.adarshr.test-logger'
9 | id 'jacoco'
10 | id "org.jlleitschuh.gradle.ktlint"
11 | id "java-test-fixtures"
12 | }
13 |
14 | java {
15 | toolchain {
16 | languageVersion.set(JavaLanguageVersion.of(8))
17 | }
18 |
19 | withSourcesJar()
20 | withJavadocJar()
21 | }
22 |
23 | dependencies {
24 | api "com.guardsquare:proguard-core:${proguardCoreVersion}"
25 |
26 | testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
27 | testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
28 | testImplementation 'dev.zacsweers.kctfork:core:0.3.0'
29 | testImplementation "io.kotest:kotest-runner-junit5-jvm:$kotestVersion" // for kotest framework
30 | testImplementation "io.kotest:kotest-assertions-core-jvm:$kotestVersion" // for kotest core jvm assertions
31 | testImplementation "io.kotest:kotest-property-jvm:$kotestVersion" // for kotest property test
32 | testImplementation 'io.mockk:mockk:1.13.5' // for mocking
33 |
34 | testImplementation(testFixtures("com.guardsquare:proguard-core:${proguardCoreVersion}")) {
35 | exclude group: 'com.guardsquare', module: 'proguard-core'
36 | }
37 | }
38 |
39 | // Early access automatic downloads are not yet supported:
40 | // https://github.com/gradle/gradle/issues/14814
41 | // But it will work if e.g. Java N-ea is pre-installed
42 | def javaVersionsForTest = 9..19
43 |
44 | test {
45 | useJUnitPlatform()
46 | }
47 |
48 | jacocoTestReport {
49 | // Define which classes need to be monitored
50 | def sources = files(sourceSets.main.allSource.srcDirs)
51 | sourceDirectories.setFrom(sources)
52 | additionalSourceDirs.setFrom(sources)
53 | sourceDirectories.setFrom(sources)
54 | def classes = files(sourceSets.main.output.classesDirs)
55 | classDirectories.setFrom(classes)
56 | executionData.setFrom project.fileTree(dir: '.', include: '**/build/jacoco/*.exec')
57 | reports {
58 | xml.required = true
59 | csv.required = true
60 | html.destination file("${buildDir}/reports/coverage")
61 | }
62 | }
63 |
64 | publishing {
65 | publications {
66 | mavenJava(MavenPublication) { publication ->
67 | groupId = 'com.guardsquare'
68 | artifactId = 'proguard-assembler'
69 | version = version
70 | pom {
71 | name = "ProGuard Assembler and Disassembler"
72 | description = 'The ProGuard Assembler and Disassembler can assemble and disassemble Java class files.'
73 | url = "https://github.com/Guardsquare/kotlin-metadata-printer"
74 | licenses {
75 | license {
76 | name = 'Apache License Version 2.0'
77 | url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
78 | distribution = 'repo'
79 | }
80 | }
81 | issueManagement {
82 | system = 'Github Tracker'
83 | url = 'https://github.com/Guardsquare/proguard-assembler/issues'
84 | }
85 | scm {
86 | url = 'https://github.com/Guardsquare/proguard-assembler.git'
87 | connection = 'scm:git:https://github.com/Guardsquare/proguard-assembler.git'
88 | }
89 | developers {
90 | developer {
91 | id = 'james.hamilton'
92 | name = 'James Hamilton'
93 | organization = 'Guardsquare'
94 | organizationUrl = 'https://www.guardsquare.com/'
95 | roles = ['Project Administrator', 'Developer']
96 | }
97 | }
98 | }
99 |
100 | from components.java
101 |
102 | if (project.hasProperty('PROGUARD_SIGNING_KEY')) {
103 | // We use in-memory ascii-armored keys
104 | // See https://docs.gradle.org/current/userguide/signing_plugin.html#sec:in-memory-keys
105 | signing {
106 | String key = project.findProperty('PROGUARD_SIGNING_KEY')
107 | String password = project.findProperty('PROGUARD_SIGNING_PASSWORD')
108 | useInMemoryPgpKeys(key, password)
109 | sign publication
110 | }
111 | }
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/assembler/AssemblyConstants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.assembler;
19 |
20 | import java.text.*;
21 | import java.util.Locale;
22 |
23 | public final class AssemblyConstants
24 | {
25 | public static final String JBC_EXTENSION = ".jbc";
26 |
27 | public static final char ATTRIBUTES_OPEN = '[';
28 | public static final char ATTRIBUTES_CLOSE = ']';
29 | public static final char EQUALS = '=';
30 | public static final char COLON = ':';
31 | public static final char REFERENCE_SEPARATOR = '#';
32 | public static final char SPECIAL_METHOD_PREFIX = '<';
33 | public static final char SPECIAL_METHOD_SUFFIX = '>';
34 | public static final char STRING_QUOTE = '"';
35 | public static final char CHAR_QUOTE = '\'';
36 |
37 | public static final String ACC_ANNOTATION = "annotation";
38 |
39 | public static final String TYPE_STRING = "String";
40 | public static final String TYPE_CLASS = "Class";
41 | public static final String TYPE_METHOD_HANDLE = "MethodHandle";
42 | public static final String TYPE_METHOD_TYPE = "MethodType";
43 | public static final String TYPE_DYNAMIC = "Dynamic";
44 | public static final String TYPE_ANNOTATION = "Annotation";
45 | public static final String TYPE_ENUM = "Enum";
46 | public static final String TYPE_ARRAY = "Array";
47 | public static final String TYPE_DOUBLE = "D";
48 | public static final String TYPE_FLOAT = "F";
49 | public static final String TYPE_LONG = "L";
50 |
51 | public static final String REF_GET_FIELD = "getfield";
52 | public static final String REF_GET_STATIC = "getstatic";
53 | public static final String REF_PUT_FIELD = "putfield";
54 | public static final String REF_PUT_STATIC = "putstatic";
55 | public static final String REF_INVOKE_VIRTUAL = "invokevirtual";
56 | public static final String REF_INVOKE_STATIC = "invokestatic";
57 | public static final String REF_INVOKE_SPECIAL = "invokespecial";
58 | public static final String REF_NEW_INVOKE_SPECIAL = "newinvokespecial";
59 | public static final String REF_INVOKE_INTERFACE = "invokeinterface";
60 |
61 | public static final String TARGET_TYPE_P_A_RAMETER_GENERIC_CLASS = "parameter_generic_class";
62 | public static final String TARGET_TYPE_P_A_RAMETER_GENERIC_METHOD = "parameter_generic_method";
63 | public static final String TARGET_TYPE_E_X_TENDS = "extends";
64 | public static final String TARGET_TYPE_B_O_UND_GENERIC_CLASS = "bound_generic_class";
65 | public static final String TARGET_TYPE_B_O_UND_GENERIC_METHOD = "bound_generic_method";
66 | public static final String TARGET_TYPE_F_I_ELD = "field";
67 | public static final String TARGET_TYPE_R_E_TURN = "return";
68 | public static final String TARGET_TYPE_R_E_CEIVER = "receiver";
69 | public static final String TARGET_TYPE_P_A_RAMETER = "parameter";
70 | public static final String TARGET_TYPE_T_H_ROWS = "throws";
71 | public static final String TARGET_TYPE_L_O_CAL_VARIABLE = "local_variable";
72 | public static final String TARGET_TYPE_R_E_SOURCE_VARIABLE = "resource_variable";
73 | public static final String TARGET_TYPE_C_A_TCH = "catch";
74 | public static final String TARGET_TYPE_I_N_STANCE_OF = "instance_of";
75 | public static final String TARGET_TYPE_N_E_W = "new";
76 | public static final String TARGET_TYPE_M_E_THOD_REFERENCE_NEW = "method_reference_new";
77 | public static final String TARGET_TYPE_M_E_THOD_REFERENCE = "method_reference";
78 | public static final String TARGET_TYPE_C_A_ST = "cast";
79 | public static final String TARGET_TYPE_A_R_GUMENT_GENERIC_METHOD_NEW = "argument_generic_method_new";
80 | public static final String TARGET_TYPE_A_R_GUMENT_GENERIC_METHOD = "argument_generic_method";
81 | public static final String TARGET_TYPE_A_R_GUMENT_GENERIC_METHOD_REFERENCE_NEW = "argument_generic_method_reference_new";
82 | public static final String TARGET_TYPE_A_R_GUMENT_GENERIC_METHOD_REFERENCE = "argument_generic_method_reference";
83 |
84 | public static final String TYPE_PATH_ARRAY = "array";
85 | public static final String TYPE_PATH_NESTED = "inner_type";
86 | public static final String TYPE_PATH_TYPE_ARGUMENT_BOUND = "wildcard";
87 | public static final String TYPE_PATH_TYPE_ARGUMENT = "type_argument";
88 |
89 | public static final String IMPORT = "import";
90 | public static final String CLASS = "class";
91 | public static final String EXTENDS = "extends";
92 | public static final String IMPLEMENTS = "implements";
93 | public static final String THROWS = "throws";
94 | public static final String CATCH = "catch";
95 | public static final String CASE = "case";
96 | public static final String DEFAULT = "default";
97 | public static final String REQUIRES = "requires";
98 | public static final String EXPORTS = "exports";
99 | public static final String OPENS = "opens";
100 | public static final String USES = "uses";
101 | public static final String PROVIDES = "provides";
102 | public static final String TRUE = "true";
103 | public static final String FALSE = "false";
104 |
105 | public static final char BODY_OPEN = '{';
106 | public static final char BODY_CLOSE = '}';
107 | public static final char STATEMENT_END = ';';
108 |
109 | public static final String VERSION = "version";
110 | public static final String INIT = "init";
111 | public static final String CLINIT = "clinit";
112 | public static final String LINE = "line";
113 | public static final String ANY = "any";
114 | public static final String TO = "to";
115 | public static final String WITH = "with";
116 | public static final String AS = "as";
117 | public static final String IN = "in";
118 | public static final String LABEL = "label";
119 | public static final String LOCAL_VAR_START = "startlocalvar";
120 | public static final String LOCAL_VAR_END = "endlocalvar";
121 | public static final String LOCAL_VAR_TYPE_START = "startlocalvartype";
122 | public static final String LOCAL_VAR_TYPE_END = "endlocalvartype";
123 |
124 | public static final DecimalFormat DOUBLE_TO_STRING = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
125 |
126 | static {
127 | DOUBLE_TO_STRING.setMaximumFractionDigits(340);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/assembler/ClassMembersParser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.assembler;
19 |
20 | import proguard.classfile.*;
21 | import proguard.classfile.attribute.*;
22 | import proguard.classfile.editor.*;
23 | import proguard.classfile.util.*;
24 | import proguard.classfile.visitor.*;
25 | import proguard.util.ArrayUtil;
26 |
27 | /**
28 | * Parses ProgramClass members.
29 | *
30 | * @author Joachim Vandersmissen
31 | */
32 | public class ClassMembersParser
33 | implements ClassVisitor,
34 | MemberVisitor
35 | {
36 | private final Parser p;
37 | private final ConstantPoolEditor cpe;
38 |
39 |
40 | /**
41 | * Constructs a new ClassMembersParser that uses a Parser and a
42 | * ConstantPoolEditor.
43 | *
44 | * @param p the Parser to use to parse basic structures.
45 | * @param cpe the ConstantPoolEditor to use to add Constants to the constant
46 | * pool.
47 | */
48 | public ClassMembersParser(Parser p, ConstantPoolEditor cpe)
49 | {
50 | this.p = p;
51 | this.cpe = cpe;
52 | }
53 |
54 |
55 | // Implementations for ClassVisitor.
56 |
57 | public void visitAnyClass(Clazz clazz) {}
58 |
59 |
60 | public void visitProgramClass(ProgramClass programClass)
61 | {
62 | ClassEditor classEditor = new ClassEditor(programClass);
63 | while (!p.nextTtypeEquals(AssemblyConstants.BODY_CLOSE))
64 | {
65 | // We need to parse a lot of information before we know which
66 | // class member we're declaring.
67 | int accessFlags = p.expectAccessFlags();
68 | if (p.nextTtypeEquals(AssemblyConstants.ATTRIBUTES_OPEN) ||
69 | p.nextTtypeEquals(AssemblyConstants.BODY_OPEN))
70 | {
71 | // We know we're declaring a method now.
72 | p.pushBack();
73 |
74 | ProgramMethod method =
75 | new ProgramMethod(accessFlags,
76 | cpe.addUtf8Constant(ClassConstants.METHOD_NAME_CLINIT),
77 | cpe.addUtf8Constant(ClassConstants.METHOD_TYPE_CLINIT),
78 | null);
79 | method.accept(programClass, this);
80 |
81 | classEditor.addMethod(method);
82 | }
83 | else
84 | {
85 | String type = p.expectType("class member type");
86 | String name = p.expectMethodName("class member name");
87 | if (p.nextTtypeEquals(JavaTypeConstants.METHOD_ARGUMENTS_OPEN))
88 | {
89 | // We know we're declaring a method now.
90 | MethodParametersAttribute methodParametersAttribute =
91 | new MethodParametersAttribute();
92 | methodParametersAttribute.u2attributeNameIndex =
93 | cpe.addUtf8Constant(Attribute.METHOD_PARAMETERS);
94 | methodParametersAttribute.parameters =
95 | new ParameterInfo[0];
96 |
97 | ProgramMethod method =
98 | new ProgramMethod(accessFlags,
99 | cpe.addUtf8Constant(name),
100 | cpe.addUtf8Constant(expectMethodArguments(methodParametersAttribute) + type),
101 | null);
102 | method.accept(programClass, this);
103 |
104 | if (methodParametersAttribute.u1parametersCount > 0)
105 | {
106 | new AttributesEditor(programClass, method, false).addAttribute(methodParametersAttribute);
107 | }
108 |
109 | classEditor.addMethod(method);
110 | }
111 | else
112 | {
113 | // We know we're declaring a field now.
114 | ProgramField field =
115 | new ProgramField(accessFlags,
116 | cpe.addUtf8Constant(name),
117 | cpe.addUtf8Constant(type),
118 | null);
119 | field.accept(programClass, this);
120 |
121 | classEditor.addField(field);
122 | }
123 | }
124 | }
125 | }
126 |
127 |
128 | // Implementations for MemberVisitor.
129 |
130 | public void visitProgramField(ProgramClass programClass, ProgramField programField)
131 | {
132 | if (p.nextTtypeEquals(AssemblyConstants.EQUALS))
133 | {
134 | ConstantValueAttribute constantValueAttribute =
135 | new ConstantValueAttribute(cpe.addUtf8Constant(Attribute.CONSTANT_VALUE),
136 | p.expectLoadableConstant(programClass, cpe, new ConstantParser(p, cpe)));
137 |
138 | new AttributesEditor(programClass, programField, false).addAttribute(constantValueAttribute);
139 | }
140 |
141 | if (p.nextTtypeEquals(AssemblyConstants.ATTRIBUTES_OPEN))
142 | {
143 | programField.accept(programClass, new AttributesParser(p, cpe));
144 | }
145 |
146 | p.expect(AssemblyConstants.STATEMENT_END, "field end");
147 | }
148 |
149 |
150 | public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
151 | {
152 |
153 | if (p.expectIfNextTtypeEqualsWord(AssemblyConstants.THROWS))
154 | {
155 | ExceptionsAttribute exceptionsAttribute =
156 | new ExceptionsAttribute();
157 | exceptionsAttribute.u2attributeNameIndex =
158 | cpe.addUtf8Constant(Attribute.EXCEPTIONS);
159 | exceptionsAttribute.u2exceptionIndexTable = new int[0];
160 |
161 | do
162 | {
163 | String exceptionType =
164 | ClassUtil.internalClassName(p.expectWord("exception class"));
165 |
166 | exceptionsAttribute.u2exceptionIndexTable =
167 | ArrayUtil.add(exceptionsAttribute.u2exceptionIndexTable,
168 | exceptionsAttribute.u2exceptionIndexTableLength++,
169 | cpe.addClassConstant(exceptionType, null));
170 | } while (p.nextTtypeEquals(JavaTypeConstants.METHOD_ARGUMENTS_SEPARATOR));
171 |
172 | new AttributesEditor(programClass, programMethod, false).addAttribute(exceptionsAttribute);
173 | }
174 |
175 | if (p.nextTtypeEquals(AssemblyConstants.ATTRIBUTES_OPEN))
176 | {
177 | programMethod.accept(programClass, new AttributesParser(p, cpe));
178 | }
179 |
180 | if (p.nextTtypeEquals(AssemblyConstants.BODY_OPEN))
181 | {
182 | programMethod.accept(programClass, new InstructionsParser(p, cpe, new CodeAttributeComposer(true, true, false)));
183 | }
184 | else
185 | {
186 | p.expect(AssemblyConstants.STATEMENT_END, "method end");
187 | }
188 | }
189 |
190 |
191 | // Small utility methods.
192 |
193 | /**
194 | * Parses ProgramMethod arguments. This method advances the Parser.
195 | *
196 | * @param methodParametersAttribute the MethodParametersAttribute to add the
197 | * method parameters to.
198 | * @return the method arguments descriptor.
199 | */
200 | private String expectMethodArguments(MethodParametersAttribute methodParametersAttribute)
201 | {
202 | StringBuilder methodArguments = new StringBuilder();
203 | methodArguments.append(JavaTypeConstants.METHOD_ARGUMENTS_OPEN);
204 | if (p.nextTtypeEquals(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE))
205 | {
206 | return methodArguments.append(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE).toString();
207 | }
208 |
209 | while (true)
210 | {
211 | // Syntactic sugar: parameters access/names in method descriptor.
212 | ParameterInfo parameterInfo = new ParameterInfo();
213 | parameterInfo.u2accessFlags = p.expectAccessFlags();
214 | methodArguments.append(p.expectType("method parameter type"));
215 |
216 | // Name index is optional.
217 | if (p.nextTtypeEqualsWord())
218 | {
219 | parameterInfo.u2nameIndex = cpe.addUtf8Constant(p.sval);
220 | }
221 |
222 | methodParametersAttribute.parameters =
223 | ArrayUtil.add(methodParametersAttribute.parameters, methodParametersAttribute.u1parametersCount++, parameterInfo);
224 |
225 | if (p.nextTtypeEquals(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE))
226 | {
227 | break;
228 | }
229 |
230 | p.expect(JavaTypeConstants.METHOD_ARGUMENTS_SEPARATOR, "method arguments separator");
231 | }
232 |
233 | // Check if any of the parameters actually has a name or access flags.
234 | for (int index = 0; index < methodParametersAttribute.u1parametersCount; index++)
235 | {
236 | ParameterInfo parameterInfo = methodParametersAttribute.parameters[index];
237 | if (parameterInfo.u2accessFlags != 0 ||
238 | parameterInfo.u2nameIndex != 0)
239 | {
240 | return methodArguments.append(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE).toString();
241 | }
242 | }
243 |
244 | // No parameters with name or access flag so we set the count to 0.
245 | methodParametersAttribute.u1parametersCount = 0;
246 | return methodArguments.append(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE).toString();
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/assembler/ClassParser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.assembler;
19 |
20 | import proguard.classfile.AccessConstants;
21 | import proguard.classfile.ClassConstants;
22 | import proguard.classfile.Clazz;
23 | import proguard.classfile.JavaTypeConstants;
24 | import proguard.classfile.JavaVersionConstants;
25 | import proguard.classfile.ProgramClass;
26 | import proguard.classfile.ProgramField;
27 | import proguard.classfile.ProgramMethod;
28 | import proguard.classfile.attribute.Attribute;
29 | import proguard.classfile.constant.Constant;
30 | import proguard.classfile.editor.ClassEditor;
31 | import proguard.classfile.editor.ConstantPoolEditor;
32 | import proguard.classfile.util.ClassUtil;
33 | import proguard.classfile.visitor.ClassVisitor;
34 |
35 | /**
36 | * Parses a ProgramClass.
37 | *
38 | * @author Joachim Vandersmissen
39 | */
40 | public class ClassParser
41 | implements ClassVisitor
42 | {
43 | private final Parser p;
44 |
45 |
46 | /**
47 | * Constructs a new ClassParser that uses a Parser.
48 | *
49 | * @param p the Parser to use to parse basic structures.
50 | */
51 | public ClassParser(Parser p)
52 | {
53 | this.p = p;
54 | }
55 |
56 |
57 | // Implementations for ClassVisitor.
58 |
59 | public void visitAnyClass(Clazz clazz) {}
60 |
61 |
62 | public void visitProgramClass(ProgramClass programClass)
63 | {
64 | programClass.u2constantPoolCount = 1;
65 | programClass.constantPool = new Constant[1];
66 | programClass.u2interfaces = new int[0];
67 | programClass.fields = new ProgramField[0];
68 | programClass.methods = new ProgramMethod[0];
69 | programClass.attributes = new Attribute[0];
70 |
71 | while (p.nextTtypeEqualsWord())
72 | {
73 | String keyword = p.sval;
74 | if (AssemblyConstants.IMPORT.equals(keyword))
75 | {
76 | String className = p.expectWord("class name");
77 | p.expect(AssemblyConstants.STATEMENT_END, "import end");
78 | int index = className.lastIndexOf(JavaTypeConstants.PACKAGE_SEPARATOR);
79 | if (index > -1)
80 | {
81 | p.imports.put(className.substring(index + 1), className);
82 | }
83 | }
84 | else if (AssemblyConstants.VERSION.equals(keyword))
85 | {
86 | programClass.u4version =
87 | ClassUtil.internalClassVersion(AssemblyConstants.DOUBLE_TO_STRING.format(p.expectNumber("version number")));
88 | p.expect(AssemblyConstants.STATEMENT_END, "version end");
89 | }
90 | else
91 | {
92 | p.pushBack();
93 | break;
94 | }
95 | }
96 |
97 | if (programClass.u4version == 0)
98 | {
99 | programClass.u4version = ClassUtil.internalClassVersion(JavaVersionConstants.CLASS_VERSION_1_8);
100 | }
101 |
102 | ClassEditor classEditor = new ClassEditor(programClass);
103 | ConstantPoolEditor cpe = new ConstantPoolEditor(programClass);
104 |
105 | programClass.u2accessFlags = p.expectClassAccessFlags();
106 | programClass.u2thisClass =
107 | cpe.addClassConstant(ClassUtil.internalClassName(p.expectWord("this class")), null);
108 |
109 | // Syntactic sugar: extends in interfaces define bytecode interfaces.
110 | if ((programClass.u2accessFlags & AccessConstants.INTERFACE) != 0)
111 | {
112 | if (p.expectIfNextTtypeEqualsWord(AssemblyConstants.EXTENDS))
113 | {
114 | expectInterfaces(classEditor, cpe);
115 | }
116 | }
117 | else
118 | {
119 | if (p.nextTtypeEqualsWord()) {
120 | if (p.sval.equals(AssemblyConstants.EXTENDS))
121 | {
122 | programClass.u2superClass =
123 | cpe.addClassConstant(ClassUtil.internalClassName(p.expectWord("super class")), null);
124 | }
125 | else
126 | {
127 | p.pushBack();
128 | }
129 | }
130 |
131 | while (p.expectIfNextTtypeEqualsWord(AssemblyConstants.IMPLEMENTS))
132 | {
133 | expectInterfaces(classEditor, cpe);
134 | }
135 | }
136 |
137 | // Syntactic sugar: default superclasses.
138 | if (programClass.u2superClass == 0)
139 | {
140 | if ((programClass.u2accessFlags & AccessConstants.ENUM) != 0)
141 | {
142 | // Syntactic sugar: default java.lang.Enum superclass for enums.
143 | programClass.u2superClass =
144 | cpe.addClassConstant(ClassConstants.NAME_JAVA_LANG_ENUM, null);
145 | }
146 | else if ((programClass.u2accessFlags & AccessConstants.MODULE) == 0 &&
147 | !ClassConstants.NAME_JAVA_LANG_OBJECT.equals(programClass.getName()))
148 | {
149 | // Syntactic sugar: default java.lang.Object superclass for any
150 | // other class type *except* modules and java.lang.Object itself.
151 | programClass.u2superClass =
152 | cpe.addClassConstant(ClassConstants.NAME_JAVA_LANG_OBJECT, null);
153 | }
154 | }
155 |
156 | // Syntactic sugar: default interface for annotations.
157 | if ((programClass.u2accessFlags & AccessConstants.ANNOTATION) != 0 &&
158 | !containsInterface(programClass, ClassConstants.NAME_JAVA_LANG_ANNOTATION_ANNOTATION))
159 | {
160 | classEditor.addInterface(cpe.addClassConstant(ClassConstants.NAME_JAVA_LANG_ANNOTATION_ANNOTATION, null));
161 | }
162 |
163 | if (p.nextTtypeEquals(AssemblyConstants.ATTRIBUTES_OPEN))
164 | {
165 | programClass.accept(new AttributesParser(p, cpe));
166 | }
167 |
168 | if (p.nextTtypeEquals(AssemblyConstants.BODY_OPEN))
169 | {
170 | programClass.accept(new ClassMembersParser(p, cpe));
171 | }
172 | else
173 | {
174 | p.expect(AssemblyConstants.STATEMENT_END, "class end");
175 | }
176 | }
177 |
178 |
179 | // Small utility methods.
180 |
181 | /**
182 | * Parses interfaces.
183 | *
184 | * @param classEditor The ClassEditor to add the interfaces to.
185 | * @param cpe The ConstantPoolEditor to use to add constants.
186 | */
187 | private void expectInterfaces(ClassEditor classEditor, ConstantPoolEditor cpe)
188 | {
189 | do
190 | {
191 | classEditor.addInterface(cpe.addClassConstant(ClassUtil.internalClassName(p.expectWord("interface class")), null));
192 | } while (p.nextTtypeEquals(','));
193 | }
194 |
195 |
196 | /**
197 | * Returns whether a given ProgramClass contains an interface.
198 | *
199 | * @param programClass the ProgramClass to check.
200 | * @param interf the internal name of the interface.
201 | * @return true if the ProgramClass contains the interface, false otherwise.
202 | */
203 | private boolean containsInterface(ProgramClass programClass, String interf)
204 | {
205 | for (int index = 0; index < programClass.u2interfacesCount; index++)
206 | {
207 | if (interf.equals(programClass.getInterfaceName(index)))
208 | {
209 | return true;
210 | }
211 | }
212 |
213 | return false;
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/assembler/ConstantParser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.assembler;
19 |
20 | import proguard.classfile.*;
21 | import proguard.classfile.constant.*;
22 | import proguard.classfile.constant.visitor.ConstantVisitor;
23 | import proguard.classfile.editor.*;
24 | import proguard.classfile.util.*;
25 |
26 | /**
27 | * Parses Constants, adds them to the constant pool and sets the index field.
28 | *
29 | * @author Joachim Vandersmissen
30 | */
31 | public class ConstantParser
32 | implements ConstantVisitor
33 | {
34 | private static final int INTEGER_FALSE = 1;
35 | private static final int INTEGER_TRUE = 1;
36 |
37 |
38 | private final Parser p;
39 | private final ConstantPoolEditor cpe;
40 |
41 | private int index;
42 |
43 |
44 | /**
45 | * Constructs a new ConstantParser that uses a Parser and a
46 | * ConstantPoolEditor.
47 | *
48 | * @param p the Parser to use to parse basic structures.
49 | * @param cpe the ConstantPoolEditor to use to add Constants to the constant
50 | * pool.
51 | */
52 | public ConstantParser(Parser p, ConstantPoolEditor cpe)
53 | {
54 | this.p = p;
55 | this.cpe = cpe;
56 | }
57 |
58 |
59 | public int getIndex()
60 | {
61 | return index;
62 | }
63 |
64 |
65 | // Implementations for ConstantVisitor.
66 |
67 | public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
68 | {
69 | if (p.nextTtypeEqualsChar())
70 | {
71 | if (p.sval.length() > 1)
72 | {
73 | throw new ParseException("Char value contains multiple characters.", p.lineno());
74 | }
75 |
76 | index = cpe.addIntegerConstant((int) p.sval.charAt(0));
77 | return;
78 | }
79 |
80 | if (p.nextTtypeEqualsNumber())
81 | {
82 | index = cpe.addIntegerConstant((int) p.nval);
83 | return;
84 | }
85 |
86 | if (AssemblyConstants.TRUE.equals(p.expect(AssemblyConstants.TRUE, AssemblyConstants.FALSE)))
87 | {
88 | index = cpe.addIntegerConstant(INTEGER_TRUE);
89 | return;
90 | }
91 |
92 | // Keyword is FALSE
93 | index = cpe.addIntegerConstant(INTEGER_FALSE);
94 | }
95 |
96 |
97 | public void visitLongConstant(Clazz clazz, LongConstant longConstant)
98 | {
99 | index = cpe.addLongConstant((long) p.expectNumber("long value"));
100 | }
101 |
102 |
103 | public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
104 | {
105 | index = cpe.addFloatConstant((float) p.expectNumber("float value"));
106 | }
107 |
108 |
109 | public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
110 | {
111 | index = cpe.addDoubleConstant(p.expectNumber("double value"));
112 | }
113 |
114 |
115 | public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
116 | {
117 | index = cpe.addStringConstant(p.expectString("string value"), null, null);
118 | }
119 |
120 |
121 | public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
122 | {
123 | // Should only be used when an actual "string" (surrounded by quotes) is
124 | // expected.
125 | index = cpe.addUtf8Constant(p.expectString("string value"));
126 | }
127 |
128 |
129 | public void visitDynamicConstant(Clazz clazz, DynamicConstant dynamicConstant)
130 | {
131 | int bootstrapMethodAttributeIndex =
132 | (int) p.expectNumber("dynamic bootstrap method index");
133 | String type = p.expectType("dynamic type");
134 | String name = p.expectWord("dynamic name");
135 | index = cpe.addDynamicConstant(bootstrapMethodAttributeIndex,
136 | cpe.addNameAndTypeConstant(name, type),
137 | null);
138 | }
139 |
140 |
141 | public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
142 | {
143 | int bootstrapMethodAttributeIndex =
144 | (int) p.expectNumber("invokedynamic bootstrap method index");
145 | String returnType = p.expectType("invokedynamic return type");
146 | String name = p.expectMethodName("invokedynamic name");
147 | String methodArgs = p.expectMethodArguments("invokedynamic arguments");
148 | index = cpe.addInvokeDynamicConstant(bootstrapMethodAttributeIndex,
149 | cpe.addNameAndTypeConstant(name, methodArgs + returnType),
150 | null);
151 | }
152 |
153 |
154 | public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
155 | {
156 | String referenceKind = p.expectWord("reference kind");
157 | int refKind;
158 | RefConstant refConstant;
159 | switch (referenceKind)
160 | {
161 | case AssemblyConstants.REF_GET_FIELD: refKind = MethodHandleConstant.REF_GET_FIELD; refConstant = new FieldrefConstant(); break;
162 | case AssemblyConstants.REF_GET_STATIC: refKind = MethodHandleConstant.REF_GET_STATIC; refConstant = new FieldrefConstant(); break;
163 | case AssemblyConstants.REF_PUT_FIELD: refKind = MethodHandleConstant.REF_PUT_FIELD; refConstant = new FieldrefConstant(); break;
164 | case AssemblyConstants.REF_PUT_STATIC: refKind = MethodHandleConstant.REF_PUT_STATIC; refConstant = new FieldrefConstant(); break;
165 | case AssemblyConstants.REF_INVOKE_VIRTUAL: refKind = MethodHandleConstant.REF_INVOKE_VIRTUAL; refConstant = new MethodrefConstant(); break;
166 | case AssemblyConstants.REF_INVOKE_STATIC: refKind = MethodHandleConstant.REF_INVOKE_STATIC; refConstant = new MethodrefConstant(); break;
167 | case AssemblyConstants.REF_INVOKE_SPECIAL: refKind = MethodHandleConstant.REF_INVOKE_SPECIAL; refConstant = new MethodrefConstant(); break;
168 | case AssemblyConstants.REF_NEW_INVOKE_SPECIAL: refKind = MethodHandleConstant.REF_NEW_INVOKE_SPECIAL; refConstant = new MethodrefConstant(); break;
169 | case AssemblyConstants.REF_INVOKE_INTERFACE: refKind = MethodHandleConstant.REF_INVOKE_INTERFACE; refConstant = new InterfaceMethodrefConstant(); break;
170 | default: throw new ParseException("Unknown reference kind " + referenceKind + ".", p.lineno());
171 | }
172 |
173 | refConstant.accept(clazz, this);
174 | index = cpe.addMethodHandleConstant(refKind, index);
175 | }
176 |
177 |
178 | public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant)
179 | {
180 | // Modules are not encoded in internal form like class and interface
181 | // names...
182 | index = cpe.addModuleConstant(p.expectWord("module name"));
183 | }
184 |
185 |
186 | public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant)
187 | {
188 | index = cpe.addPackageConstant(ClassUtil.externalPackageName(p.expectWord("package name")));
189 | }
190 |
191 |
192 | public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
193 | {
194 | int classIndex;
195 | if (p.nextTtypeEquals(AssemblyConstants.REFERENCE_SEPARATOR))
196 | {
197 | classIndex = ((ProgramClass) clazz).u2thisClass;
198 | }
199 | else
200 | {
201 | new ClassConstant().accept(clazz, this);
202 | classIndex = index;
203 | p.expect(AssemblyConstants.REFERENCE_SEPARATOR, "fieldref separator");
204 | }
205 |
206 | String type = p.expectType("fieldref type");
207 | String name = p.expectWord("fieldref name");
208 | index = cpe.addFieldrefConstant(classIndex, name, type, null, null);
209 | }
210 |
211 |
212 | public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
213 | {
214 | int classIndex;
215 | if (p.nextTtypeEquals(AssemblyConstants.REFERENCE_SEPARATOR))
216 | {
217 | classIndex = ((ProgramClass) clazz).u2thisClass;
218 | }
219 | else
220 | {
221 | new ClassConstant().accept(clazz, this);
222 | classIndex = index;
223 | p.expect(AssemblyConstants.REFERENCE_SEPARATOR, "methodref separator");
224 | }
225 |
226 | String returnType = p.expectType("methodref return type");
227 | String name = p.expectMethodName("methodref name");
228 | String methodArgs = p.expectMethodArguments("methodref arguments");
229 | index = cpe.addMethodrefConstant(classIndex, name, methodArgs + returnType, null, null);
230 | }
231 |
232 |
233 | public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
234 | {
235 | int classIndex;
236 | if (p.nextTtypeEquals(AssemblyConstants.REFERENCE_SEPARATOR))
237 | {
238 | classIndex = ((ProgramClass) clazz).u2thisClass;
239 | }
240 | else
241 | {
242 | new ClassConstant().accept(clazz, this);
243 | classIndex = index;
244 | p.expect(AssemblyConstants.REFERENCE_SEPARATOR, "interfacemethodref separator");
245 | }
246 |
247 | String returnType = p.expectType("interfacemethodref return type");
248 | String name = p.expectMethodName("interfacemethodref name");
249 | String methodArgs = p.expectMethodArguments("interfacemethodref arguments");
250 | index = cpe.addInterfaceMethodrefConstant(classIndex, name, methodArgs + returnType, null, null);
251 | }
252 |
253 |
254 | public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
255 | {
256 | index = cpe.addClassConstant(ClassUtil.internalClassTypeFromType(p.expectType("class name")), null);
257 | }
258 |
259 |
260 | public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
261 | {
262 | String returnType = p.expectType("method return type");
263 | String methodArgs = p.expectMethodArguments("method arguments");
264 | index = cpe.addMethodTypeConstant(methodArgs + returnType, null);
265 | }
266 |
267 |
268 | public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
269 | {
270 | throw new ParseException("Unsupported operation", p.lineno());
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/assembler/ParseException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.assembler;
19 |
20 | /**
21 | * General purpose parsing exception.
22 | *
23 | * @author Joachim Vandersmissen
24 | */
25 | public class ParseException extends RuntimeException
26 | {
27 | /**
28 | * Constructs a new ParseException with a message and the line number at
29 | * which the exception occured. The provided line number will be prepended
30 | * to the message.
31 | *
32 | * @param message the message to add to the exception.
33 | * @param line the line number at which the exception occured.
34 | */
35 | public ParseException(String message, int line)
36 | {
37 | super("Line " + line + ": " + message);
38 | }
39 |
40 |
41 | /**
42 | * Constructs a new ParseException with a message, the line number at which
43 | * the exception occured, and the cause of the exception. The provided line
44 | * number will be prepended to the message.
45 | *
46 | * @param message the message to add to the exception.
47 | * @param line the line number at which the exception occured.
48 | * @param cause the cause of the exception.
49 | */
50 | public ParseException(String message, int line, Throwable cause)
51 | {
52 | super("Line " + line + ": " + message, cause);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/assembler/io/JbcReader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.assembler.io;
19 |
20 | import com.guardsquare.proguard.assembler.ClassParser;
21 | import com.guardsquare.proguard.assembler.Parser;
22 | import proguard.classfile.Clazz;
23 | import proguard.classfile.ProgramClass;
24 | import proguard.classfile.visitor.ClassVisitor;
25 | import proguard.io.DataEntry;
26 | import proguard.io.DataEntryReader;
27 |
28 | import java.io.IOException;
29 | import java.io.InputStream;
30 | import java.io.InputStreamReader;
31 | import java.nio.charset.StandardCharsets;
32 |
33 | /**
34 | * This DataEntryReader parses jbc files using the ClassParser, and then applies
35 | * a given ClassVisitor to the parsed ProgramClass objects.
36 | *
37 | * @author Joachim Vandersmissen
38 | */
39 | public class JbcReader implements DataEntryReader
40 | {
41 | private final ClassVisitor classVisitor;
42 |
43 |
44 | /**
45 | * Creates a new JbcReader.
46 | *
47 | * @param classVisitor The class visitor to apply after conversion from JBC to Clazz.
48 | */
49 | public JbcReader(ClassVisitor classVisitor)
50 | {
51 | this.classVisitor = classVisitor;
52 | }
53 |
54 |
55 | // Implementations for DataEntryReader.
56 |
57 | public void read(DataEntry dataEntry) throws IOException
58 | {
59 | try
60 | {
61 | // Get the input stream.
62 | InputStream inputStream = dataEntry.getInputStream();
63 |
64 | // Wrap it into an input stream reader.
65 | InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
66 |
67 | // Create a Clazz representation.
68 | Clazz clazz = new ProgramClass();
69 | clazz.accept(new ClassParser(new Parser(inputStreamReader)));
70 |
71 | // Apply the visitor.
72 | clazz.accept(classVisitor);
73 |
74 | dataEntry.closeInputStream();
75 | }
76 | catch (Exception e)
77 | {
78 | throw new IOException("An exception occured while assembling " + dataEntry.getName(), e);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/AnnotationsPrinter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler;
19 |
20 | import com.guardsquare.proguard.assembler.AssemblyConstants;
21 | import proguard.classfile.*;
22 | import proguard.classfile.attribute.CodeAttribute;
23 | import proguard.classfile.attribute.annotation.*;
24 | import proguard.classfile.attribute.annotation.target.*;
25 | import proguard.classfile.attribute.annotation.target.visitor.*;
26 | import proguard.classfile.attribute.annotation.visitor.*;
27 | import proguard.classfile.attribute.visitor.AttributeVisitor;
28 |
29 | /**
30 | * Prints Annotations and TypeAnnotations.
31 | *
32 | * @author Joachim Vandersmissen
33 | */
34 | public class AnnotationsPrinter
35 | implements AttributeVisitor,
36 | AnnotationVisitor,
37 | TypeAnnotationVisitor,
38 | TargetInfoVisitor,
39 | LocalVariableTargetElementVisitor,
40 | TypePathInfoVisitor,
41 | ElementValueVisitor
42 | {
43 | private final Printer p;
44 |
45 |
46 | /**
47 | * Constructs a new AnnotationsPrinter that uses a Printer.
48 | *
49 | * @param p the Printer to use to print basic structures.
50 | */
51 | public AnnotationsPrinter(Printer p)
52 | {
53 | this.p = p;
54 | }
55 |
56 |
57 | // Implementations for AttributeVisitor.
58 |
59 | public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
60 | {
61 | p.printSpace();
62 | p.print(AssemblyConstants.BODY_OPEN);
63 | if (annotationsAttribute.u2annotationsCount > 0)
64 | {
65 | p.println();
66 | p.indent();
67 | for (int index = 0; index < annotationsAttribute.u2annotationsCount; index++)
68 | {
69 | p.printIndent();
70 | visitAnnotation(clazz, annotationsAttribute.annotations[index]);
71 | p.println();
72 | }
73 |
74 | p.outdent();
75 | p.printIndent();
76 | }
77 |
78 | p.print(AssemblyConstants.BODY_CLOSE);
79 | }
80 |
81 |
82 | public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
83 | {
84 | p.printSpace();
85 | p.print(AssemblyConstants.BODY_OPEN);
86 | if (parameterAnnotationsAttribute.u1parametersCount > 0)
87 | {
88 | p.println();
89 | p.indent();
90 | for (int parameter = 0; parameter < parameterAnnotationsAttribute.u1parametersCount; parameter++)
91 | {
92 | p.printIndent();
93 | p.print(AssemblyConstants.BODY_OPEN);
94 | if (parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameter] > 0)
95 | {
96 | p.println();
97 | p.indent();
98 | for (int index = 0; index < parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameter]; index++)
99 | {
100 | p.printIndent();
101 | visitAnnotation(clazz, parameterAnnotationsAttribute.parameterAnnotations[parameter][index]);
102 | p.println();
103 | }
104 |
105 | p.outdent();
106 | p.printIndent();
107 | }
108 |
109 | p.print(AssemblyConstants.BODY_CLOSE);
110 | p.println();
111 | }
112 |
113 | p.outdent();
114 | p.printIndent();
115 | }
116 |
117 | p.print(AssemblyConstants.BODY_CLOSE);
118 | }
119 |
120 |
121 | public void visitAnyTypeAnnotationsAttribute(Clazz clazz, TypeAnnotationsAttribute typeAnnotationsAttribute)
122 | {
123 | p.printSpace();
124 | p.print(AssemblyConstants.BODY_OPEN);
125 | if (typeAnnotationsAttribute.u2annotationsCount > 0)
126 | {
127 | p.println();
128 | p.indent();
129 | for (int index = 0; index < typeAnnotationsAttribute.u2annotationsCount; index++)
130 | {
131 | p.printIndent();
132 | visitTypeAnnotation(clazz, (TypeAnnotation) typeAnnotationsAttribute.annotations[index]);
133 | p.println();
134 | }
135 |
136 | p.outdent();
137 | p.printIndent();
138 | }
139 |
140 | p.print(AssemblyConstants.BODY_CLOSE);
141 | }
142 |
143 |
144 | public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
145 | {
146 | p.printSpace();
147 | annotationDefaultAttribute.defaultValue.accept(clazz, null, this);
148 | }
149 |
150 |
151 | // Implementations for AnnotationVisitor.
152 |
153 | public void visitAnnotation(Clazz clazz, Annotation annotation)
154 | {
155 | p.printType(annotation.getType(clazz));
156 | p.printSpace();
157 | p.print(AssemblyConstants.BODY_OPEN);
158 | if (annotation.u2elementValuesCount > 0)
159 | {
160 | p.println();
161 | p.indent();
162 | for (int index = 0; index < annotation.u2elementValuesCount; index++)
163 | {
164 | ElementValue elementValue = annotation.elementValues[index];
165 | p.printIndent();
166 | p.printWord(elementValue.getMethodName(clazz));
167 | p.printSpace();
168 | p.print(AssemblyConstants.EQUALS);
169 | p.printSpace();
170 | elementValue.accept(clazz, annotation, this);
171 | p.println();
172 | }
173 |
174 | p.outdent();
175 | p.printIndent();
176 | }
177 |
178 | p.print(AssemblyConstants.BODY_CLOSE);
179 | }
180 |
181 |
182 | // Implementations for TypeAnnotationVisitor.
183 |
184 | public void visitTypeAnnotation(Clazz clazz, TypeAnnotation typeAnnotation)
185 | {
186 | p.printType(typeAnnotation.getType(clazz));
187 | p.printSpace();
188 | p.print(AssemblyConstants.BODY_OPEN);
189 | if (typeAnnotation.u2elementValuesCount > 0)
190 | {
191 | p.println();
192 | p.indent();
193 | for (int index = 0; index < typeAnnotation.u2elementValuesCount; index++)
194 | {
195 | ElementValue elementValue = typeAnnotation.elementValues[index];
196 | p.printIndent();
197 | p.printWord(elementValue.getMethodName(clazz));
198 | p.printSpace();
199 | p.print(AssemblyConstants.EQUALS);
200 | p.printSpace();
201 | elementValue.accept(clazz, typeAnnotation, this);
202 | p.println();
203 | }
204 |
205 | p.outdent();
206 | p.printIndent();
207 | }
208 |
209 | p.print(AssemblyConstants.BODY_CLOSE);
210 | p.printSpace();
211 | printTargetInfoType(typeAnnotation.targetInfo.u1targetType);
212 | typeAnnotation.targetInfoAccept(clazz, this);
213 | p.printSpace();
214 | p.print(AssemblyConstants.BODY_OPEN);
215 | if (typeAnnotation.typePath.length > 0)
216 | {
217 | p.println();
218 | p.indent();
219 | for (int index = 0; index < typeAnnotation.typePath.length; index++)
220 | {
221 | p.printIndent();
222 | visitTypePathInfo(clazz, typeAnnotation, typeAnnotation.typePath[index]);
223 | p.println();
224 | }
225 |
226 | p.outdent();
227 | p.printIndent();
228 | }
229 |
230 | p.print(AssemblyConstants.BODY_CLOSE);
231 | }
232 |
233 |
234 | // Implementations for TargetInfoVisitor.
235 |
236 | public void visitTypeParameterTargetInfo(Clazz clazz, TypeAnnotation typeAnnotation, TypeParameterTargetInfo typeParameterTargetInfo)
237 | {
238 | p.printSpace();
239 | p.printNumber(typeParameterTargetInfo.u1typeParameterIndex);
240 | }
241 |
242 |
243 | public void visitSuperTypeTargetInfo(Clazz clazz, TypeAnnotation typeAnnotation, SuperTypeTargetInfo superTypeTargetInfo)
244 | {
245 | p.printSpace();
246 | p.printNumber(superTypeTargetInfo.u2superTypeIndex);
247 | }
248 |
249 |
250 | public void visitTypeParameterBoundTargetInfo(Clazz clazz, TypeAnnotation typeAnnotation, TypeParameterBoundTargetInfo typeParameterBoundTargetInfo)
251 | {
252 | p.printSpace();
253 | p.printNumber(typeParameterBoundTargetInfo.u1typeParameterIndex);
254 | p.printSpace();
255 | p.printNumber(typeParameterBoundTargetInfo.u1boundIndex);
256 | }
257 |
258 |
259 | public void visitEmptyTargetInfo(Clazz clazz, Member member, TypeAnnotation typeAnnotation, EmptyTargetInfo emptyTargetInfo)
260 | {
261 | // NOP.
262 | }
263 |
264 |
265 | public void visitFormalParameterTargetInfo(Clazz clazz, Method method, TypeAnnotation typeAnnotation, FormalParameterTargetInfo formalParameterTargetInfo)
266 | {
267 | p.printSpace();
268 | p.printNumber(formalParameterTargetInfo.u1formalParameterIndex);
269 | }
270 |
271 |
272 | public void visitThrowsTargetInfo(Clazz clazz, Method method, TypeAnnotation typeAnnotation, ThrowsTargetInfo throwsTargetInfo)
273 | {
274 | p.printSpace();
275 | p.printNumber(throwsTargetInfo.u2throwsTypeIndex);
276 | }
277 |
278 |
279 | public void visitLocalVariableTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo)
280 | {
281 | p.printSpace();
282 | p.print(AssemblyConstants.BODY_OPEN);
283 | if (localVariableTargetInfo.u2tableLength > 0)
284 | {
285 | p.println();
286 | p.indent();
287 | for (int index = 0; index < localVariableTargetInfo.u2tableLength; index++)
288 | {
289 | p.printIndent();
290 | visitLocalVariableTargetElement(clazz, method, codeAttribute, typeAnnotation, localVariableTargetInfo, localVariableTargetInfo.table[index]);
291 | p.println();
292 | }
293 |
294 | p.outdent();
295 | p.printIndent();
296 | }
297 |
298 | p.print(AssemblyConstants.BODY_CLOSE);
299 | }
300 |
301 |
302 | public void visitCatchTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, CatchTargetInfo catchTargetInfo)
303 | {
304 | p.printSpace();
305 | p.printNumber(catchTargetInfo.u2exceptionTableIndex);
306 | }
307 |
308 |
309 | public void visitOffsetTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, OffsetTargetInfo offsetTargetInfo)
310 | {
311 | p.printSpace();
312 | p.printOffset(offsetTargetInfo.u2offset);
313 | }
314 |
315 |
316 | public void visitTypeArgumentTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, TypeArgumentTargetInfo typeArgumentTargetInfo)
317 | {
318 | p.printSpace();
319 | p.printOffset(typeArgumentTargetInfo.u2offset);
320 | p.printSpace();
321 | p.printNumber(typeArgumentTargetInfo.u1typeArgumentIndex);
322 | }
323 |
324 |
325 | // Implementations for TypePathInfoVisitor.
326 |
327 | public void visitTypePathInfo(Clazz clazz, TypeAnnotation typeAnnotation, TypePathInfo typePathInfo)
328 | {
329 | printTypePathKind(typePathInfo.u1typePathKind);
330 |
331 | // Type argument index is optional.
332 | if (typePathInfo.u1typeArgumentIndex != 0)
333 | {
334 | p.printSpace();
335 | p.printNumber(typePathInfo.u1typeArgumentIndex);
336 | }
337 |
338 | p.print(AssemblyConstants.STATEMENT_END);
339 | }
340 |
341 |
342 | // Implementations for LocalVariableTargetElementVisitor.
343 |
344 | public void visitLocalVariableTargetElement(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo, LocalVariableTargetElement localVariableTargetElement)
345 | {
346 | p.printOffset(localVariableTargetElement.u2startPC);
347 | p.printSpace();
348 | p.printOffset(localVariableTargetElement.u2startPC + localVariableTargetElement.u2length);
349 | p.printSpace();
350 | p.printNumber(localVariableTargetElement.u2index);
351 | p.print(AssemblyConstants.STATEMENT_END);
352 | }
353 |
354 |
355 | // Implementations for ElementValueVisitor.
356 |
357 | public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
358 | {
359 | clazz.constantPoolEntryAccept(constantElementValue.u2constantValueIndex, new ConstantPrinter(p, constantElementValue.u1tag));
360 | p.print(AssemblyConstants.STATEMENT_END);
361 | }
362 |
363 |
364 | public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
365 | {
366 | p.printType(enumConstantElementValue.getTypeName(clazz));
367 | p.print(AssemblyConstants.REFERENCE_SEPARATOR);
368 | p.printWord(enumConstantElementValue.getConstantName(clazz));
369 | p.print(AssemblyConstants.STATEMENT_END);
370 | }
371 |
372 |
373 | public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
374 | {
375 | clazz.constantPoolEntryAccept(classElementValue.u2classInfoIndex, new ConstantPrinter(p, false));
376 | p.print(AssemblyConstants.STATEMENT_END);
377 | }
378 |
379 |
380 | public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
381 | {
382 | p.print(ElementValue.TAG_ANNOTATION);
383 | visitAnnotation(clazz, annotationElementValue.annotationValue);
384 | }
385 |
386 |
387 | public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
388 | {
389 | p.print(AssemblyConstants.BODY_OPEN);
390 | if (arrayElementValue.u2elementValuesCount > 0)
391 | {
392 | p.println();
393 | p.indent();
394 | for (int index = 0; index < arrayElementValue.u2elementValuesCount; index++)
395 | {
396 | p.printIndent();
397 | arrayElementValue.elementValues[index].accept(clazz, annotation, this);
398 | p.println();
399 | }
400 |
401 | p.outdent();
402 | p.printIndent();
403 | }
404 |
405 | p.print(AssemblyConstants.BODY_CLOSE);
406 | }
407 |
408 |
409 | // Small utility methods.
410 |
411 | /**
412 | * Prints the representation of a TargetInfo type.
413 | *
414 | * @param type the TargetInfo type.
415 | * @throws PrintException if the type is an unknown TargetInfo type.
416 | */
417 | private void printTargetInfoType(int type)
418 | {
419 | switch (type)
420 | {
421 | case TargetInfo.TARGET_TYPE_PARAMETER_GENERIC_CLASS: p.printWord(AssemblyConstants.TARGET_TYPE_P_A_RAMETER_GENERIC_CLASS); break;
422 | case TargetInfo.TARGET_TYPE_PARAMETER_GENERIC_METHOD: p.printWord(AssemblyConstants.TARGET_TYPE_P_A_RAMETER_GENERIC_METHOD); break;
423 | case TargetInfo.TARGET_TYPE_EXTENDS: p.printWord(AssemblyConstants.TARGET_TYPE_E_X_TENDS); break;
424 | case TargetInfo.TARGET_TYPE_BOUND_GENERIC_CLASS: p.printWord(AssemblyConstants.TARGET_TYPE_B_O_UND_GENERIC_CLASS); break;
425 | case TargetInfo.TARGET_TYPE_BOUND_GENERIC_METHOD: p.printWord(AssemblyConstants.TARGET_TYPE_B_O_UND_GENERIC_METHOD); break;
426 | case TargetInfo.TARGET_TYPE_FIELD: p.printWord(AssemblyConstants.TARGET_TYPE_F_I_ELD); break;
427 | case TargetInfo.TARGET_TYPE_RETURN: p.printWord(AssemblyConstants.TARGET_TYPE_R_E_TURN); break;
428 | case TargetInfo.TARGET_TYPE_RECEIVER: p.printWord(AssemblyConstants.TARGET_TYPE_R_E_CEIVER); break;
429 | case TargetInfo.TARGET_TYPE_PARAMETER: p.printWord(AssemblyConstants.TARGET_TYPE_P_A_RAMETER); break;
430 | case TargetInfo.TARGET_TYPE_THROWS: p.printWord(AssemblyConstants.TARGET_TYPE_T_H_ROWS); break;
431 | case TargetInfo.TARGET_TYPE_LOCAL_VARIABLE: p.printWord(AssemblyConstants.TARGET_TYPE_L_O_CAL_VARIABLE); break;
432 | case TargetInfo.TARGET_TYPE_RESOURCE_VARIABLE: p.printWord(AssemblyConstants.TARGET_TYPE_R_E_SOURCE_VARIABLE); break;
433 | case TargetInfo.TARGET_TYPE_CATCH: p.printWord(AssemblyConstants.TARGET_TYPE_C_A_TCH); break;
434 | case TargetInfo.TARGET_TYPE_INSTANCE_OF: p.printWord(AssemblyConstants.TARGET_TYPE_I_N_STANCE_OF); break;
435 | case TargetInfo.TARGET_TYPE_NEW: p.printWord(AssemblyConstants.TARGET_TYPE_N_E_W); break;
436 | case TargetInfo.TARGET_TYPE_METHOD_REFERENCE_NEW: p.printWord(AssemblyConstants.TARGET_TYPE_M_E_THOD_REFERENCE_NEW); break;
437 | case TargetInfo.TARGET_TYPE_METHOD_REFERENCE: p.printWord(AssemblyConstants.TARGET_TYPE_M_E_THOD_REFERENCE); break;
438 | case TargetInfo.TARGET_TYPE_CAST: p.printWord(AssemblyConstants.TARGET_TYPE_C_A_ST); break;
439 | case TargetInfo.TARGET_TYPE_ARGUMENT_GENERIC_METHODNew: p.printWord(AssemblyConstants.TARGET_TYPE_A_R_GUMENT_GENERIC_METHOD_NEW); break;
440 | case TargetInfo.TARGET_TYPE_ARGUMENT_GENERIC_METHOD: p.printWord(AssemblyConstants.TARGET_TYPE_A_R_GUMENT_GENERIC_METHOD); break;
441 | case TargetInfo.TARGET_TYPE_ARGUMENT_GENERIC_METHODReferenceNew: p.printWord(AssemblyConstants.TARGET_TYPE_A_R_GUMENT_GENERIC_METHOD_REFERENCE_NEW); break;
442 | case TargetInfo.TARGET_TYPE_ARGUMENT_GENERIC_METHODReference: p.printWord(AssemblyConstants.TARGET_TYPE_A_R_GUMENT_GENERIC_METHOD_REFERENCE); break;
443 | default: throw new PrintException("Unknown target info type " + type + ".");
444 | }
445 | }
446 |
447 |
448 | /**
449 | * Prints the representation of a TypePathInfo kind.
450 | *
451 | * @param kind the TypePathInfo kind.
452 | * @throws PrintException if the kind is an unknown TypePathInfo kind.
453 | */
454 | private void printTypePathKind(int kind)
455 | {
456 | switch (kind)
457 | {
458 | case TypePathInfo.KIND_Array: p.printWord(AssemblyConstants.TYPE_PATH_ARRAY); break;
459 | case TypePathInfo.KIND_Nested: p.printWord(AssemblyConstants.TYPE_PATH_NESTED); break;
460 | case TypePathInfo.KIND_TypeArgumentBound: p.printWord(AssemblyConstants.TYPE_PATH_TYPE_ARGUMENT_BOUND); break;
461 | case TypePathInfo.KIND_TypeArgument: p.printWord(AssemblyConstants.TYPE_PATH_TYPE_ARGUMENT); break;
462 | default: throw new PrintException("Unknown type path kind " + kind + ".");
463 | }
464 | }
465 | }
466 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/ClassMembersPrinter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler;
19 |
20 | import com.guardsquare.proguard.assembler.AssemblyConstants;
21 | import proguard.classfile.*;
22 | import proguard.classfile.attribute.*;
23 | import proguard.classfile.editor.AttributesEditor;
24 | import proguard.classfile.util.*;
25 | import proguard.classfile.visitor.*;
26 |
27 | import java.util.*;
28 |
29 | /**
30 | * Prints ProgramClass members.
31 | *
32 | * @author Joachim Vandersmissen
33 | */
34 | public class ClassMembersPrinter
35 | implements ClassVisitor,
36 | MemberVisitor
37 | {
38 | private final Printer p;
39 |
40 |
41 | /**
42 | * Constructs a new ClassMembersPrinter that uses a Printer.
43 | *
44 | * @param p the Printer to use to print basic structures.
45 | */
46 | public ClassMembersPrinter(Printer p)
47 | {
48 | this.p = p;
49 | }
50 |
51 |
52 | // Implementations for ClassVisitor.
53 |
54 | public void visitAnyClass(Clazz clazz) {}
55 |
56 |
57 | public void visitProgramClass(ProgramClass programClass)
58 | {
59 | if (programClass.u2fieldsCount > 0 ||
60 | programClass.u2methodsCount > 0)
61 | {
62 | p.printSpace();
63 | p.print(AssemblyConstants.BODY_OPEN);
64 | p.println();
65 | p.indent();
66 | for (int index = 0; index < programClass.u2fieldsCount; index++)
67 | {
68 | ProgramField field = programClass.fields[index];
69 | p.printIndent();
70 | if (printFieldAccessFlags(field.u2accessFlags))
71 | {
72 | p.printSpace();
73 | }
74 |
75 | p.printType(field.getDescriptor(programClass));
76 | p.printSpace();
77 | p.printWord(field.getName(programClass));
78 | field.accept(programClass, this);
79 | p.println();
80 | }
81 |
82 | p.println();
83 | for (int index = 0; index < programClass.u2methodsCount; index++)
84 | {
85 | ProgramMethod method = programClass.methods[index];
86 | p.printIndent();
87 | boolean space = printMethodAccessFlags(method.u2accessFlags);
88 | if (!ClassConstants.METHOD_NAME_CLINIT.equals(method.getName(programClass)) ||
89 | !ClassConstants.METHOD_TYPE_CLINIT.equals(method.getDescriptor(programClass)))
90 | {
91 | if (space)
92 | {
93 | p.printSpace();
94 | }
95 |
96 | p.printMethodReturnType(method.getDescriptor(programClass));
97 | p.printSpace();
98 | p.printWord(method.getName(programClass));
99 | printMethodArguments(programClass,
100 | method.getDescriptor(programClass),
101 | (MethodParametersAttribute) new AttributesEditor(programClass, method, false).findAttribute(Attribute.METHOD_PARAMETERS));
102 | }
103 |
104 | method.accept(programClass, this);
105 | p.println();
106 | p.println();
107 | }
108 |
109 | p.print(AssemblyConstants.BODY_CLOSE);
110 | }
111 | else
112 | {
113 | p.print(AssemblyConstants.STATEMENT_END);
114 | }
115 | }
116 |
117 |
118 | // Implementations for MemberVisitor.
119 |
120 | public void visitProgramField(ProgramClass programClass, ProgramField programField)
121 | {
122 | ConstantValueAttribute constantValueAttribute =
123 | (ConstantValueAttribute) new AttributesEditor(programClass,
124 | programField,
125 | false).findAttribute(Attribute.CONSTANT_VALUE);
126 | if (constantValueAttribute != null)
127 | {
128 | p.printSpace();
129 | p.print(AssemblyConstants.EQUALS);
130 | p.printSpace();
131 | programClass.constantPoolEntryAccept(constantValueAttribute.u2constantValueIndex, new ConstantPrinter(p, true));
132 | }
133 |
134 | programField.accept(programClass, new AttributesPrinter(p));
135 | p.print(AssemblyConstants.STATEMENT_END);
136 | }
137 |
138 |
139 | public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
140 | {
141 | ExceptionsAttribute exceptionsAttribute =
142 | (ExceptionsAttribute) new AttributesEditor(programClass,
143 | programMethod,
144 | false).findAttribute(Attribute.EXCEPTIONS);
145 | if (exceptionsAttribute != null &&
146 | exceptionsAttribute.u2exceptionIndexTableLength > 0)
147 | {
148 | p.printSpace();
149 | p.printWord(AssemblyConstants.THROWS);
150 | p.printSpace();
151 | for (int index = 0; index < exceptionsAttribute.u2exceptionIndexTableLength; index++)
152 | {
153 | p.printWord(ClassUtil.externalClassName(programClass.getClassName(exceptionsAttribute.u2exceptionIndexTable[index])));
154 | if (index < exceptionsAttribute.u2exceptionIndexTableLength - 1)
155 | {
156 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_SEPARATOR);
157 | p.printSpace();
158 | }
159 | }
160 | }
161 |
162 | programMethod.accept(programClass, new AttributesPrinter(p));
163 | if (programMethod.processingInfo != null)
164 | {
165 | // Use shorthand notation.
166 | CodeAttribute codeAttribute = ((CodeAttribute) programMethod.processingInfo);
167 | Map oldLabels = p.labels;
168 | p.labels = new HashMap<>();
169 | codeAttribute.accept(programClass, programMethod, new LabelsCollector(p.labels));
170 | codeAttribute.accept(programClass, programMethod, new InstructionsPrinter(p));
171 | p.labels = oldLabels;
172 | }
173 | else
174 | {
175 | p.print(AssemblyConstants.STATEMENT_END);
176 | }
177 | }
178 |
179 |
180 | // Small utility methods.
181 |
182 | /**
183 | * Prints ProgramMethod arguments.
184 | *
185 | * @param clazz the class being disassembled.
186 | * @param methodDescriptor the internal method descriptor
187 | * containing the method return type and
188 | * the method arguments.
189 | * @param methodParametersAttribute the MethodParametersAttribute to read
190 | * the parameters from.
191 | */
192 | private void printMethodArguments(Clazz clazz, String methodDescriptor, MethodParametersAttribute methodParametersAttribute)
193 | {
194 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_OPEN);
195 | int index = 0;
196 | InternalTypeEnumeration internalTypeEnumeration =
197 | new InternalTypeEnumeration(methodDescriptor);
198 | while (internalTypeEnumeration.hasMoreTypes())
199 | {
200 | String type = internalTypeEnumeration.nextType();
201 | if (methodParametersAttribute != null &&
202 | index < methodParametersAttribute.u1parametersCount)
203 | {
204 | if (p.printAccessFlags(methodParametersAttribute.parameters[index].u2accessFlags))
205 | {
206 | p.printSpace();
207 | }
208 | }
209 |
210 | p.printType(type);
211 |
212 | if (methodParametersAttribute != null &&
213 | index < methodParametersAttribute.u1parametersCount)
214 | {
215 | ParameterInfo parameterInfo =
216 | methodParametersAttribute.parameters[index++];
217 | // Name index is optional.
218 | if (parameterInfo.u2nameIndex != 0)
219 | {
220 | p.printSpace();
221 | p.printWord(parameterInfo.getName(clazz));
222 | }
223 | }
224 |
225 | if (internalTypeEnumeration.hasMoreTypes())
226 | {
227 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_SEPARATOR);
228 | p.printSpace();
229 | }
230 | }
231 |
232 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE);
233 | }
234 |
235 |
236 | /**
237 | * Prints ProgramField access flags.
238 | *
239 | * @param accessFlags the access flags.
240 | * @return true if at least one access flag was written, false otherwise.
241 | */
242 | public boolean printFieldAccessFlags(int accessFlags)
243 | {
244 | if (accessFlags == 0)
245 | {
246 | return false;
247 | }
248 |
249 | StringBuilder stringBuilder = new StringBuilder();
250 | if ((accessFlags & AccessConstants.PUBLIC) != 0) stringBuilder.append(JavaAccessConstants.PUBLIC).append(' ');
251 | if ((accessFlags & AccessConstants.PRIVATE) != 0) stringBuilder.append(JavaAccessConstants.PRIVATE).append(' ');
252 | if ((accessFlags & AccessConstants.PROTECTED) != 0) stringBuilder.append(JavaAccessConstants.PROTECTED).append(' ');
253 | if ((accessFlags & AccessConstants.STATIC) != 0) stringBuilder.append(JavaAccessConstants.STATIC).append(' ');
254 | if ((accessFlags & AccessConstants.FINAL) != 0) stringBuilder.append(JavaAccessConstants.FINAL).append(' ');
255 | if ((accessFlags & AccessConstants.VOLATILE) != 0) stringBuilder.append(JavaAccessConstants.VOLATILE).append(' ');
256 | if ((accessFlags & AccessConstants.TRANSIENT) != 0) stringBuilder.append(JavaAccessConstants.TRANSIENT).append(' ');
257 | if ((accessFlags & AccessConstants.SYNTHETIC) != 0) stringBuilder.append(JavaAccessConstants.SYNTHETIC).append(' ');
258 | if ((accessFlags & AccessConstants.ENUM) != 0) stringBuilder.append(JavaAccessConstants.ENUM).append(' ');
259 | p.printWord(stringBuilder.toString().trim());
260 | return true;
261 | }
262 |
263 |
264 | /**
265 | * Prints ProgramMethod access flags.
266 | *
267 | * @param accessFlags the access flags.
268 | * @return true if at least one access flag was written, false otherwise.
269 | */
270 | public boolean printMethodAccessFlags(int accessFlags)
271 | {
272 | if (accessFlags == 0)
273 | {
274 | return false;
275 | }
276 |
277 | StringBuilder stringBuilder = new StringBuilder();
278 | if ((accessFlags & AccessConstants.PUBLIC) != 0) stringBuilder.append(JavaAccessConstants.PUBLIC).append(' ');
279 | if ((accessFlags & AccessConstants.PRIVATE) != 0) stringBuilder.append(JavaAccessConstants.PRIVATE).append(' ');
280 | if ((accessFlags & AccessConstants.PROTECTED) != 0) stringBuilder.append(JavaAccessConstants.PROTECTED).append(' ');
281 | if ((accessFlags & AccessConstants.STATIC) != 0) stringBuilder.append(JavaAccessConstants.STATIC).append(' ');
282 | if ((accessFlags & AccessConstants.FINAL) != 0) stringBuilder.append(JavaAccessConstants.FINAL).append(' ');
283 | if ((accessFlags & AccessConstants.SYNCHRONIZED) != 0) stringBuilder.append(JavaAccessConstants.SYNCHRONIZED).append(' ');
284 | if ((accessFlags & AccessConstants.BRIDGE) != 0) stringBuilder.append(JavaAccessConstants.BRIDGE).append(' ');
285 | if ((accessFlags & AccessConstants.VARARGS) != 0) stringBuilder.append(JavaAccessConstants.VARARGS).append(' ');
286 | if ((accessFlags & AccessConstants.NATIVE) != 0) stringBuilder.append(JavaAccessConstants.NATIVE).append(' ');
287 | if ((accessFlags & AccessConstants.ABSTRACT) != 0) stringBuilder.append(JavaAccessConstants.ABSTRACT).append(' ');
288 | if ((accessFlags & AccessConstants.STRICT) != 0) stringBuilder.append(JavaAccessConstants.STRICT).append(' ');
289 | if ((accessFlags & AccessConstants.SYNTHETIC) != 0) stringBuilder.append(JavaAccessConstants.SYNTHETIC).append(' ');
290 | p.printWord(stringBuilder.toString().trim());
291 | return true;
292 | }
293 | }
294 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/ClassPrinter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler;
19 |
20 | import com.guardsquare.proguard.assembler.AssemblyConstants;
21 | import proguard.classfile.*;
22 | import proguard.classfile.util.*;
23 | import proguard.classfile.visitor.ClassVisitor;
24 |
25 | /**
26 | * Prints a ProgramClass.
27 | *
28 | * @author Joachim Vandersmissen
29 | */
30 | public class ClassPrinter
31 | implements ClassVisitor
32 | {
33 | private final Printer p;
34 |
35 |
36 | /**
37 | * Constructs a new ClassPrinter that uses a Printer.
38 | *
39 | * @param p the Printer to use to print basic structures.
40 | */
41 | public ClassPrinter(Printer p)
42 | {
43 | this.p = p;
44 | }
45 |
46 |
47 | // Implementations for ClassVisitor.
48 |
49 | public void visitAnyClass(Clazz clazz) {}
50 |
51 |
52 | public void visitProgramClass(ProgramClass programClass)
53 | {
54 | p.printWord(AssemblyConstants.VERSION);
55 | p.printSpace();
56 | p.printWord(ClassUtil.externalClassVersion(programClass.u4version));
57 | p.print(AssemblyConstants.STATEMENT_END);
58 | p.println();
59 | if (p.printClassAccessFlags(programClass.u2accessFlags))
60 | {
61 | p.printSpace();
62 | }
63 |
64 | p.printWord(ClassUtil.externalClassName(programClass.getName()));
65 | // Syntactic sugar: extends in interfaces define bytecode interfaces.
66 | if ((programClass.u2accessFlags & AccessConstants.INTERFACE) != 0)
67 | {
68 | if (programClass.u2interfacesCount > 0)
69 | {
70 | p.printSpace();
71 | p.printWord(AssemblyConstants.EXTENDS);
72 | p.printSpace();
73 | printInterfaces(programClass);
74 | }
75 | }
76 | else
77 | {
78 | if (programClass.u2superClass != 0)
79 | {
80 | p.printSpace();
81 | p.printWord(AssemblyConstants.EXTENDS);
82 | p.printSpace();
83 | p.printWord(ClassUtil.externalClassName(programClass.getSuperName()));
84 | }
85 |
86 | if (programClass.u2interfacesCount > 0)
87 | {
88 | p.printSpace();
89 | p.printWord(AssemblyConstants.IMPLEMENTS);
90 | p.printSpace();
91 | printInterfaces(programClass);
92 | }
93 | }
94 |
95 | programClass.accept(new AttributesPrinter(p));
96 | programClass.accept(new ClassMembersPrinter(p));
97 |
98 | p.flush();
99 | }
100 |
101 |
102 | // Small utility methods.
103 |
104 | /**
105 | * Prints the interfaces of a class.
106 | *
107 | * @param programClass the class to print the interfaces of.
108 | */
109 | private void printInterfaces(ProgramClass programClass)
110 | {
111 | for (int index = 0; index < programClass.u2interfacesCount; index++)
112 | {
113 | p.printWord(ClassUtil.externalClassName(programClass.getInterfaceName(index)));
114 | if (index < programClass.u2interfacesCount - 1)
115 | {
116 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_SEPARATOR);
117 | p.printSpace();
118 | }
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/ConstantPrinter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler;
19 |
20 | import com.guardsquare.proguard.assembler.AssemblyConstants;
21 | import proguard.classfile.*;
22 | import proguard.classfile.constant.*;
23 | import proguard.classfile.constant.visitor.ConstantVisitor;
24 | import proguard.classfile.util.*;
25 |
26 | /**
27 | * Prints Constants.
28 | *
29 | * @author Joachim Vandersmissen
30 | */
31 | public class ConstantPrinter
32 | implements ConstantVisitor
33 | {
34 | private static final int INTEGER_FALSE = 0;
35 | private static final int INTEGER_TRUE = 1;
36 |
37 |
38 | private final Printer p;
39 | private final char intType;
40 | private final boolean printFullType;
41 |
42 |
43 | /**
44 | * Constructs a new ClassPrinter that uses a Printer and a boolean flag.
45 | *
46 | * @param p the Printer to use to print basic structures.
47 | * @param printFullType if this is set to true, the full type of a constant
48 | * will be printed if that constant would not be
49 | * unambiguously parsed by the Assembler.
50 | */
51 | public ConstantPrinter(Printer p, boolean printFullType)
52 | {
53 | this.p = p;
54 | this.intType = 0;
55 | this.printFullType = printFullType;
56 | }
57 |
58 |
59 | /**
60 | * Constructs a new ClassPrinter that uses a Printer, the type of the
61 | * IntegerConstant, and a boolean flag.
62 | *
63 | * @param p the Printer to use to print basic structures.
64 | * @param intType the internal type of the IntegerConstants being visited.
65 | */
66 | public ConstantPrinter(Printer p, char intType)
67 | {
68 | this.p = p;
69 | this.intType = intType;
70 | this.printFullType = false;
71 | }
72 |
73 |
74 | // Implementations for ConstantVisitor.
75 |
76 | public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
77 | {
78 | if (intType == TypeConstants.BOOLEAN)
79 | {
80 | if (integerConstant.u4value == INTEGER_TRUE)
81 | {
82 | p.printWord(AssemblyConstants.TRUE);
83 | return;
84 | }
85 |
86 | if (integerConstant.u4value == INTEGER_FALSE)
87 | {
88 | p.printWord(AssemblyConstants.FALSE);
89 | return;
90 | }
91 | }
92 |
93 | if (intType == TypeConstants.CHAR)
94 | {
95 | p.printChar((char) integerConstant.u4value);
96 | return;
97 | }
98 |
99 | if (intType != 0 && intType != TypeConstants.INT)
100 | {
101 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_OPEN);
102 | p.printType(String.valueOf(intType));
103 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE);
104 | p.printSpace();
105 | }
106 |
107 | p.printNumber(integerConstant.u4value);
108 | }
109 |
110 |
111 | public void visitLongConstant(Clazz clazz, LongConstant longConstant)
112 | {
113 | p.printNumber(longConstant.u8value);
114 | p.printWord(AssemblyConstants.TYPE_LONG);
115 | }
116 |
117 |
118 | public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
119 | {
120 | p.printNumber(floatConstant.f4value);
121 | p.printWord(AssemblyConstants.TYPE_FLOAT);
122 | }
123 |
124 |
125 | public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
126 | {
127 | p.printNumber(doubleConstant.f8value);
128 | p.printWord(AssemblyConstants.TYPE_DOUBLE);
129 | }
130 |
131 |
132 | public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
133 | {
134 | p.printString(stringConstant.getString(clazz));
135 | }
136 |
137 |
138 | public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
139 | {
140 | p.printString(utf8Constant.getString());
141 | }
142 |
143 |
144 | public void visitDynamicConstant(Clazz clazz, DynamicConstant dynamicConstant)
145 | {
146 | if (printFullType)
147 | {
148 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_OPEN);
149 | p.printWord(AssemblyConstants.TYPE_DYNAMIC);
150 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE);
151 | p.printSpace();
152 | }
153 |
154 | p.printNumber(dynamicConstant.u2bootstrapMethodAttributeIndex);
155 | p.printSpace();
156 | p.printType(dynamicConstant.getType(clazz));
157 | p.printSpace();
158 | p.printWord(dynamicConstant.getName(clazz));
159 | }
160 |
161 |
162 | public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
163 | {
164 | p.printNumber(invokeDynamicConstant.u2bootstrapMethodAttributeIndex);
165 | p.printSpace();
166 | p.printMethodReturnType(invokeDynamicConstant.getType(clazz));
167 | p.printSpace();
168 | p.printWord(invokeDynamicConstant.getName(clazz));
169 | p.printMethodArguments(invokeDynamicConstant.getType(clazz));
170 | }
171 |
172 |
173 | public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
174 | {
175 | if (printFullType)
176 | {
177 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_OPEN);
178 | p.printWord(JavaConstants.TYPE_JAVA_LANG_INVOKE_METHODHANDLE);
179 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE);
180 | p.printSpace();
181 | }
182 |
183 | printReferenceKind(methodHandleConstant.u1referenceKind);
184 | p.printSpace();
185 | clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, this);
186 | }
187 |
188 |
189 | public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant)
190 | {
191 | throw new PrintException("Unsupported operation");
192 | }
193 |
194 |
195 | public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant)
196 | {
197 | throw new PrintException("Unsupported operation");
198 | }
199 |
200 |
201 | public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
202 | {
203 | if (fieldrefConstant.u2classIndex != ((ProgramClass) clazz).u2thisClass)
204 | {
205 | clazz.constantPoolEntryAccept(fieldrefConstant.u2classIndex, this);
206 | }
207 |
208 | p.print(AssemblyConstants.REFERENCE_SEPARATOR);
209 | p.printType(fieldrefConstant.getType(clazz));
210 | p.printSpace();
211 | p.printWord(fieldrefConstant.getName(clazz));
212 | }
213 |
214 |
215 | public void visitAnyMethodrefConstant(Clazz clazz, AnyMethodrefConstant anyMethodrefConstant)
216 | {
217 | if (anyMethodrefConstant.u2classIndex != ((ProgramClass) clazz).u2thisClass)
218 | {
219 | clazz.constantPoolEntryAccept(anyMethodrefConstant.u2classIndex, this);
220 | }
221 |
222 | p.print(AssemblyConstants.REFERENCE_SEPARATOR);
223 | p.printMethodReturnType(anyMethodrefConstant.getType(clazz));
224 | p.printSpace();
225 | p.printWord(anyMethodrefConstant.getName(clazz));
226 | p.printMethodArguments(anyMethodrefConstant.getType(clazz));
227 | }
228 |
229 |
230 | public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
231 | {
232 | p.printType(ClassUtil.internalTypeFromClassType(classConstant.getName(clazz)));
233 | }
234 |
235 |
236 | public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
237 | {
238 | if (printFullType)
239 | {
240 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_OPEN);
241 | p.printWord(JavaConstants.TYPE_JAVA_LANG_INVOKE_METHODTYPE);
242 | p.print(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE);
243 | p.printSpace();
244 | }
245 |
246 | p.printMethodReturnType(methodTypeConstant.getType(clazz));
247 | p.printSpace();
248 | p.printMethodArguments(methodTypeConstant.getType(clazz));
249 | }
250 |
251 |
252 | public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
253 | {
254 | throw new PrintException("Unsupported operation.");
255 | }
256 |
257 |
258 | // Small utility methods.
259 |
260 | /**
261 | * Prints the representation of a reference kind.
262 | *
263 | * @param referenceKind the reference kind.
264 | * @throws PrintException if the reference kind is unknown
265 | */
266 | private void printReferenceKind(int referenceKind)
267 | {
268 | switch (referenceKind)
269 | {
270 | case MethodHandleConstant.REF_GET_FIELD: p.printWord(AssemblyConstants.REF_GET_FIELD); break;
271 | case MethodHandleConstant.REF_GET_STATIC: p.printWord(AssemblyConstants.REF_GET_STATIC); break;
272 | case MethodHandleConstant.REF_PUT_FIELD: p.printWord(AssemblyConstants.REF_PUT_FIELD); break;
273 | case MethodHandleConstant.REF_PUT_STATIC: p.printWord(AssemblyConstants.REF_PUT_STATIC); break;
274 | case MethodHandleConstant.REF_INVOKE_VIRTUAL: p.printWord(AssemblyConstants.REF_INVOKE_VIRTUAL); break;
275 | case MethodHandleConstant.REF_INVOKE_STATIC: p.printWord(AssemblyConstants.REF_INVOKE_STATIC); break;
276 | case MethodHandleConstant.REF_INVOKE_SPECIAL: p.printWord(AssemblyConstants.REF_INVOKE_SPECIAL); break;
277 | case MethodHandleConstant.REF_NEW_INVOKE_SPECIAL: p.printWord(AssemblyConstants.REF_NEW_INVOKE_SPECIAL); break;
278 | case MethodHandleConstant.REF_INVOKE_INTERFACE: p.printWord(AssemblyConstants.REF_INVOKE_INTERFACE); break;
279 | default: throw new PrintException("Unknown reference kind " + referenceKind + ".");
280 | }
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/InstructionsPrinter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler;
19 |
20 | import com.guardsquare.proguard.assembler.AssemblyConstants;
21 | import proguard.classfile.*;
22 | import proguard.classfile.attribute.*;
23 | import proguard.classfile.attribute.visitor.*;
24 | import proguard.classfile.editor.AttributesEditor;
25 | import proguard.classfile.instruction.*;
26 | import proguard.classfile.instruction.visitor.InstructionVisitor;
27 | import proguard.classfile.util.*;
28 |
29 | import java.util.*;
30 |
31 | /**
32 | * Prints the Instructions, ExceptionInfos and LineNumberInfos of a Code
33 | * attribute.
34 | *
35 | * @author Joachim Vandersmissen
36 | */
37 | public class InstructionsPrinter
38 | implements AttributeVisitor,
39 | ExceptionInfoVisitor,
40 | LineNumberInfoVisitor,
41 | InstructionVisitor
42 | {
43 | private final Printer p;
44 |
45 |
46 | /**
47 | * Constructs a new InstructionsPrinter that uses a Printer.
48 | *
49 | * @param p the Printer to use to print basic structures.
50 | */
51 | public InstructionsPrinter(Printer p)
52 | {
53 | this.p = p;
54 | }
55 |
56 |
57 | // Implementations for AttributeVisitor.
58 |
59 | public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
60 | {
61 | AttributesEditor attributesEditor =
62 | new AttributesEditor((ProgramClass) clazz,
63 | (ProgramMethod) method,
64 | codeAttribute,
65 | false);
66 |
67 | LineNumberTableAttribute lineNumberTableAttribute =
68 | (LineNumberTableAttribute) attributesEditor.findAttribute(Attribute.LINE_NUMBER_TABLE);
69 | LocalVariableTableAttribute localVariableTableAttribute =
70 | (LocalVariableTableAttribute) attributesEditor.findAttribute(Attribute.LOCAL_VARIABLE_TABLE);
71 | LocalVariableTypeTableAttribute localVariableTypeTableAttribute =
72 | (LocalVariableTypeTableAttribute) attributesEditor.findAttribute(Attribute.LOCAL_VARIABLE_TYPE_TABLE);
73 |
74 | p.printSpace();
75 | p.print(AssemblyConstants.BODY_OPEN);
76 | if (codeAttribute.u4codeLength > 0)
77 | {
78 | p.println();
79 | p.indent();
80 | p.indent();
81 |
82 | int offset = 0;
83 |
84 | // Inclusive because we want LocalVariableInfo and
85 | // LocalVariableTypeInfo lengths to work properly.
86 | while (offset <= codeAttribute.u4codeLength)
87 | {
88 | if (p.labels.containsKey(offset))
89 | {
90 | p.outdent();
91 | p.printIndent();
92 | p.printOffset(offset);
93 | p.print(AssemblyConstants.COLON);
94 | p.println();
95 | p.indent();
96 | }
97 |
98 | if (lineNumberTableAttribute != null)
99 | {
100 | List lineNumberInfos =
101 | findAtOffset(lineNumberTableAttribute, offset);
102 | for (int index = 0; index < lineNumberInfos.size(); index++)
103 | {
104 | p.outdent();
105 | visitLineNumberInfo(clazz, method, codeAttribute, lineNumberInfos.get(index));
106 | p.indent();
107 | }
108 | }
109 |
110 | if (localVariableTableAttribute != null)
111 | {
112 | List startingAt =
113 | findStartingAtOffset(localVariableTableAttribute, offset);
114 | for (int index = 0; index < startingAt.size(); index++)
115 | {
116 | LocalVariableInfo localVariableInfo =
117 | startingAt.get(index);
118 | p.outdent();
119 | p.printIndent();
120 | p.printWord(AssemblyConstants.LOCAL_VAR_START);
121 | p.printSpace();
122 | p.printNumber(localVariableInfo.u2index);
123 | p.printSpace();
124 | p.printType(localVariableInfo.getDescriptor(clazz));
125 | p.printSpace();
126 | p.printWord(localVariableInfo.getName(clazz));
127 | p.println();
128 | p.indent();
129 | }
130 |
131 | List endingAt =
132 | findEndingAtOffset(localVariableTableAttribute, offset);
133 | for (int index = 0; index < endingAt.size(); index++)
134 | {
135 | LocalVariableInfo localVariableInfo =
136 | endingAt.get(index);
137 | p.outdent();
138 | p.printIndent();
139 | p.printWord(AssemblyConstants.LOCAL_VAR_END);
140 | p.printSpace();
141 | p.printNumber(localVariableInfo.u2index);
142 | p.println();
143 | p.indent();
144 | }
145 | }
146 |
147 | if (localVariableTypeTableAttribute != null)
148 | {
149 | List startingAt =
150 | findStartingAtOffset(localVariableTypeTableAttribute, offset);
151 | for (int index = 0; index < startingAt.size(); index++)
152 | {
153 | LocalVariableTypeInfo localVariableTypeInfo =
154 | startingAt.get(index);
155 | p.outdent();
156 | p.printIndent();
157 | p.printWord(AssemblyConstants.LOCAL_VAR_TYPE_START);
158 | p.printSpace();
159 | p.printNumber(localVariableTypeInfo.u2index);
160 | p.printSpace();
161 | p.printString(localVariableTypeInfo.getSignature(clazz));
162 | p.printSpace();
163 | p.printWord(localVariableTypeInfo.getName(clazz));
164 | p.println();
165 | p.indent();
166 | }
167 |
168 | List endingAt =
169 | findEndingAtOffset(localVariableTypeTableAttribute, offset);
170 | for (int index = 0; index < endingAt.size(); index++)
171 | {
172 | LocalVariableTypeInfo localVariableTypeInfo =
173 | endingAt.get(index);
174 | p.outdent();
175 | p.printIndent();
176 | p.printWord(AssemblyConstants.LOCAL_VAR_TYPE_END);
177 | p.printSpace();
178 | p.printNumber(localVariableTypeInfo.u2index);
179 | p.println();
180 | p.indent();
181 | }
182 | }
183 |
184 | List handlerAt =
185 | findHandlerAtOffset(codeAttribute, offset);
186 | for (int index = 0; index < handlerAt.size(); index++)
187 | {
188 | p.outdent();
189 | p.printIndent();
190 | p.printWord(AssemblyConstants.CATCH);
191 | visitExceptionInfo(clazz, method, codeAttribute, handlerAt.get(index));
192 | p.println();
193 | p.indent();
194 | }
195 |
196 | if (offset >= codeAttribute.u4codeLength)
197 | {
198 | break;
199 | }
200 |
201 | Instruction instruction =
202 | InstructionFactory.create(codeAttribute.code, offset);
203 | p.printIndent();
204 | p.printWord(instruction.getName());
205 | instruction.accept(clazz, method, codeAttribute, offset, this);
206 | offset += instruction.length(offset);
207 | p.println();
208 | }
209 |
210 | p.outdent();
211 | p.outdent();
212 | p.printIndent();
213 | }
214 |
215 | p.print(AssemblyConstants.BODY_CLOSE);
216 | }
217 |
218 |
219 | // Implementations for InstructionVisitor.
220 |
221 | public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
222 | {
223 | if (simpleInstruction.opcode == Instruction.OP_BIPUSH ||
224 | simpleInstruction.opcode == Instruction.OP_SIPUSH)
225 | {
226 | p.printSpace();
227 | p.printNumber(simpleInstruction.constant);
228 | }
229 |
230 | if (simpleInstruction.opcode == Instruction.OP_NEWARRAY)
231 | {
232 | p.printSpace();
233 | p.printType(String.valueOf(InstructionUtil.internalTypeFromArrayType((byte) simpleInstruction.constant)));
234 | }
235 | }
236 |
237 |
238 | public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
239 | {
240 | if (variableInstruction.wide)
241 | {
242 | p.printWord("_w");
243 | }
244 |
245 | if (variableInstruction.opcode >= Instruction.OP_ILOAD &&
246 | variableInstruction.opcode <= Instruction.OP_ALOAD ||
247 | variableInstruction.opcode >= Instruction.OP_ISTORE &&
248 | variableInstruction.opcode <= Instruction.OP_ASTORE ||
249 | variableInstruction.opcode == Instruction.OP_IINC ||
250 | variableInstruction.opcode == Instruction.OP_RET)
251 | {
252 | p.printSpace();
253 | p.printNumber(variableInstruction.variableIndex);
254 | }
255 |
256 | if (variableInstruction.opcode == Instruction.OP_IINC)
257 | {
258 | p.printSpace();
259 | p.printNumber(variableInstruction.constant);
260 | }
261 | }
262 |
263 |
264 | public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
265 | {
266 | p.printSpace();
267 | clazz.constantPoolEntryAccept(constantInstruction.constantIndex, new ConstantPrinter(p, true));
268 | if (constantInstruction.opcode == Instruction.OP_MULTIANEWARRAY)
269 | {
270 | p.printSpace();
271 | p.printNumber(constantInstruction.constant);
272 | }
273 | }
274 |
275 |
276 | public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
277 | {
278 | p.printSpace();
279 | p.printOffset(offset + branchInstruction.branchOffset);
280 | }
281 |
282 |
283 | public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
284 | {
285 | p.printSpace();
286 | p.print(AssemblyConstants.BODY_OPEN);
287 | p.println();
288 | p.indent();
289 | for (int index = 0; index < tableSwitchInstruction.highCase - tableSwitchInstruction.lowCase + 1; index++)
290 | {
291 | p.printIndent();
292 | p.printWord(AssemblyConstants.CASE);
293 | p.printSpace();
294 | p.printNumber(tableSwitchInstruction.lowCase + index);
295 | p.print(AssemblyConstants.COLON);
296 | p.printSpace();
297 | p.printOffset(offset + tableSwitchInstruction.jumpOffsets[index]);
298 | p.println();
299 | }
300 |
301 | p.printIndent();
302 | p.printWord(AssemblyConstants.DEFAULT);
303 | p.print(AssemblyConstants.COLON);
304 | p.printSpace();
305 | p.printOffset(offset + tableSwitchInstruction.defaultOffset);
306 | p.println();
307 | p.outdent();
308 | p.printIndent();
309 | p.print(AssemblyConstants.BODY_CLOSE);
310 | }
311 |
312 |
313 | public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
314 | {
315 | p.printSpace();
316 | p.print(AssemblyConstants.BODY_OPEN);
317 | p.println();
318 | p.indent();
319 | for (int index = 0; index < lookUpSwitchInstruction.cases.length; index++)
320 | {
321 | p.printIndent();
322 | p.printWord(AssemblyConstants.CASE);
323 | p.printSpace();
324 | p.printNumber(lookUpSwitchInstruction.cases[index]);
325 | p.print(AssemblyConstants.COLON);
326 | p.printSpace();
327 | p.printOffset(offset + lookUpSwitchInstruction.jumpOffsets[index]);
328 | p.println();
329 | }
330 |
331 | p.printIndent();
332 | p.printWord(AssemblyConstants.DEFAULT);
333 | p.print(AssemblyConstants.COLON);
334 | p.printSpace();
335 | p.printOffset(offset + lookUpSwitchInstruction.defaultOffset);
336 | p.println();
337 | p.outdent();
338 | p.printIndent();
339 | p.print(AssemblyConstants.BODY_CLOSE);
340 | }
341 |
342 |
343 | // Implementations for ExceptionInfoVisitor.
344 |
345 | public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
346 | {
347 | p.printSpace();
348 |
349 | // If u2catchType = 0, the catch block is a finally block.
350 | if (exceptionInfo.u2catchType == 0)
351 | {
352 | p.printWord(AssemblyConstants.ANY);
353 | }
354 | else
355 | {
356 | p.printType(ClassUtil.internalTypeFromClassType(clazz.getClassName(exceptionInfo.u2catchType)));
357 | }
358 |
359 | p.printSpace();
360 | p.printOffset(exceptionInfo.u2startPC);
361 | p.printSpace();
362 | p.printOffset(exceptionInfo.u2endPC);
363 | }
364 |
365 |
366 | // Implementations for LineNumberInfoVisitor.
367 |
368 | public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
369 | {
370 | p.printIndent();
371 | p.printWord(AssemblyConstants.LINE);
372 | p.printSpace();
373 | p.printNumber(lineNumberInfo.u2lineNumber);
374 | p.println();
375 | }
376 |
377 |
378 | // Small utility methods.
379 |
380 | /**
381 | * Returns all LineNumberInfos at an offset in the bytecode.
382 | *
383 | * @param lineNumberTableAttribute the LineNumberTableAttribute to search in.
384 | * @param offset the bytecode offset to search for.
385 | * @return the LineNumberInfos at the offset.
386 | */
387 | private List findAtOffset(LineNumberTableAttribute lineNumberTableAttribute, int offset)
388 | {
389 | List lineNumberInfos =
390 | new ArrayList<>(lineNumberTableAttribute.u2lineNumberTableLength);
391 | for (int index = 0; index < lineNumberTableAttribute.u2lineNumberTableLength; index++)
392 | {
393 | LineNumberInfo lineNumberInfo =
394 | lineNumberTableAttribute.lineNumberTable[index];
395 | if (lineNumberInfo.u2startPC == offset)
396 | {
397 | lineNumberInfos.add(lineNumberInfo);
398 | }
399 | }
400 |
401 | return lineNumberInfos;
402 | }
403 |
404 |
405 | /**
406 | * Returns the ExceptionInfos whose handler are at an offset in the bytecode.
407 | *
408 | * @param codeAttribute the code attribute to search in.
409 | * @param offset the bytecode offset to search for.
410 | * @return the ExceptionInfos whose handler are at the offset.
411 | */
412 | private List findHandlerAtOffset(CodeAttribute codeAttribute, int offset)
413 | {
414 | List handlerAt =
415 | new ArrayList<>(codeAttribute.u2exceptionTableLength);
416 | for (int index = 0; index < codeAttribute.u2exceptionTableLength; index++)
417 | {
418 | ExceptionInfo exceptionInfo =
419 | codeAttribute.exceptionTable[index];
420 | if (exceptionInfo.u2handlerPC == offset)
421 | {
422 | handlerAt.add(exceptionInfo);
423 | }
424 | }
425 |
426 | return handlerAt;
427 | }
428 |
429 |
430 | /**
431 | * Returns all LocalVariableInfos starting at an offset in the bytecode.
432 | *
433 | * @param localVariableTableAttribute the LocalVariableTableAttribute to
434 | * search in.
435 | * @param offset the bytecode offset to search for.
436 | * @return the LocalVariableInfos starting at the offset.
437 | */
438 | private List findStartingAtOffset(LocalVariableTableAttribute localVariableTableAttribute, int offset)
439 | {
440 | List startingAt =
441 | new ArrayList<>(localVariableTableAttribute.u2localVariableTableLength);
442 | for (int index = 0; index < localVariableTableAttribute.u2localVariableTableLength; index++)
443 | {
444 | LocalVariableInfo localVariableInfo =
445 | localVariableTableAttribute.localVariableTable[index];
446 | if (localVariableInfo.u2startPC == offset)
447 | {
448 | startingAt.add(localVariableInfo);
449 | }
450 | }
451 |
452 | return startingAt;
453 | }
454 |
455 |
456 | /**
457 | * Returns all LocalVariableInfos ending at an offset in the bytecode.
458 | *
459 | * @param localVariableTableAttribute the LocalVariableTableAttribute to
460 | * search in.
461 | * @param offset the bytecode offset to search for.
462 | * @return the LocalVariableInfos ending at the offset.
463 | */
464 | private List findEndingAtOffset(LocalVariableTableAttribute localVariableTableAttribute, int offset)
465 | {
466 | List endingAt =
467 | new ArrayList<>(localVariableTableAttribute.u2localVariableTableLength);
468 | for (int index = 0; index < localVariableTableAttribute.u2localVariableTableLength; index++)
469 | {
470 | LocalVariableInfo localVariableInfo =
471 | localVariableTableAttribute.localVariableTable[index];
472 | if (localVariableInfo.u2startPC + localVariableInfo.u2length == offset)
473 | {
474 | endingAt.add(localVariableInfo);
475 | }
476 | }
477 |
478 | return endingAt;
479 | }
480 |
481 |
482 | /**
483 | * Returns all LocalVariableTypeInfos starting at an offset in the bytecode.
484 | *
485 | * @param localVariableTypeTableAttribute the LocalVariableTypeTableAttribute
486 | * to search in.
487 | * @param offset the bytecode offset to search for.
488 | * @return the LocalVariableTypeInfos starting at the offset.
489 | */
490 | private List findStartingAtOffset(LocalVariableTypeTableAttribute localVariableTypeTableAttribute, int offset)
491 | {
492 | List startingAt =
493 | new ArrayList<>(localVariableTypeTableAttribute.u2localVariableTypeTableLength);
494 | for (int index = 0; index < localVariableTypeTableAttribute.u2localVariableTypeTableLength; index++)
495 | {
496 | LocalVariableTypeInfo localVariableTypeInfo =
497 | localVariableTypeTableAttribute.localVariableTypeTable[index];
498 | if (localVariableTypeInfo.u2startPC == offset)
499 | {
500 | startingAt.add(localVariableTypeInfo);
501 | }
502 | }
503 |
504 | return startingAt;
505 | }
506 |
507 |
508 | /**
509 | * Returns all LocalVariableTypeInfos ending at an offset in the bytecode.
510 | *
511 | * @param localVariableTypeTableAttribute the LocalVariableTypeTableAttribute
512 | * to search in.
513 | * @param offset the bytecode offset to search for.
514 | * @return the LocalVariableTypeInfos ending at the offset.
515 | */
516 | private List findEndingAtOffset(LocalVariableTypeTableAttribute localVariableTypeTableAttribute, int offset)
517 | {
518 | List endingAt =
519 | new ArrayList<>(localVariableTypeTableAttribute.u2localVariableTypeTableLength);
520 | for (int index = 0; index < localVariableTypeTableAttribute.u2localVariableTypeTableLength; index++)
521 | {
522 | LocalVariableTypeInfo localVariableTypeInfo =
523 | localVariableTypeTableAttribute.localVariableTypeTable[index];
524 | if (localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length == offset)
525 | {
526 | endingAt.add(localVariableTypeInfo);
527 | }
528 | }
529 |
530 | return endingAt;
531 | }
532 | }
533 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/LabelsCollector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler;
19 |
20 | import com.guardsquare.proguard.assembler.AssemblyConstants;
21 | import proguard.classfile.*;
22 | import proguard.classfile.attribute.*;
23 | import proguard.classfile.attribute.annotation.*;
24 | import proguard.classfile.attribute.annotation.target.*;
25 | import proguard.classfile.attribute.annotation.target.visitor.*;
26 | import proguard.classfile.attribute.annotation.visitor.TypeAnnotationVisitor;
27 | import proguard.classfile.attribute.preverification.*;
28 | import proguard.classfile.attribute.visitor.*;
29 | import proguard.classfile.instruction.*;
30 | import proguard.classfile.instruction.visitor.InstructionVisitor;
31 |
32 | import java.util.Map;
33 |
34 | /**
35 | * Visits relevant offsets in a CodeAttribute and transforms them to labels in
36 | * a map.
37 | *
38 | * @author Joachim Vandersmissen
39 | */
40 | public class LabelsCollector
41 | implements AttributeVisitor,
42 | ExceptionInfoVisitor,
43 | TypeAnnotationVisitor,
44 | TargetInfoVisitor,
45 | LocalVariableTargetElementVisitor,
46 | InstructionVisitor
47 | {
48 | private final Map labels;
49 |
50 |
51 | /**
52 | * Constructs a new LabelsCollector.
53 | *
54 | * @param labels the map to collect the labels in.
55 | */
56 | public LabelsCollector(Map labels)
57 | {
58 | this.labels = labels;
59 | }
60 |
61 |
62 | // Implementations for AttributeVisitor.
63 |
64 | public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
65 |
66 |
67 | public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
68 | {
69 | codeAttribute.instructionsAccept(clazz, method, this);
70 | codeAttribute.exceptionsAccept(clazz, method, this);
71 | codeAttribute.attributesAccept(clazz, method, this);
72 | }
73 |
74 |
75 | public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
76 | {
77 | // For now we don't look at StackMap attributes, as they aren't written
78 | // to the output anyway.
79 | }
80 |
81 |
82 | public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
83 | {
84 | // For now we don't look at StackMapTable attributes, as they aren't written
85 | // to the output anyway.
86 | }
87 |
88 |
89 | public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
90 | {
91 | // Line numbers are handled inline.
92 | }
93 |
94 |
95 | public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
96 | {
97 | // Local variables are handled inline.
98 | }
99 |
100 |
101 | public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
102 | {
103 | // Local variable types are handled inline.
104 | }
105 |
106 |
107 | public void visitAnyTypeAnnotationsAttribute(Clazz clazz, TypeAnnotationsAttribute typeAnnotationsAttribute)
108 | {
109 | typeAnnotationsAttribute.typeAnnotationsAccept(clazz, this);
110 | }
111 |
112 |
113 | // Implementations for InstructionsVisitor.
114 |
115 | public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
116 |
117 |
118 | public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
119 | {
120 | collectOffset(offset + branchInstruction.branchOffset);
121 | }
122 |
123 |
124 | public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
125 | {
126 | collectOffset(offset + switchInstruction.defaultOffset);
127 | for (int index = 0; index < switchInstruction.jumpOffsets.length; index++)
128 | {
129 | collectOffset(offset + switchInstruction.jumpOffsets[index]);
130 | }
131 | }
132 |
133 |
134 | // Implementations for TypeAnnotationVisitor.
135 |
136 | public void visitTypeAnnotation(Clazz clazz, TypeAnnotation typeAnnotation)
137 | {
138 | typeAnnotation.targetInfoAccept(clazz, this);
139 | }
140 |
141 |
142 | // Implementations for TargetInfoVisitor.
143 |
144 | public void visitAnyTargetInfo(Clazz clazz, TypeAnnotation typeAnnotation, TargetInfo targetInfo) {}
145 |
146 |
147 | public void visitLocalVariableTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo)
148 | {
149 | localVariableTargetInfo.targetElementsAccept(clazz, method, codeAttribute, typeAnnotation, this);
150 | }
151 |
152 |
153 | public void visitOffsetTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, OffsetTargetInfo offsetTargetInfo)
154 | {
155 | collectOffset(offsetTargetInfo.u2offset);
156 | }
157 |
158 |
159 | public void visitTypeArgumentTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, TypeArgumentTargetInfo typeArgumentTargetInfo)
160 | {
161 | collectOffset(typeArgumentTargetInfo.u2offset);
162 | }
163 |
164 |
165 | // Implementations for LocalVariableTargetElementVisitor.
166 |
167 | public void visitLocalVariableTargetElement(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo, LocalVariableTargetElement localVariableTargetElement)
168 | {
169 | collectOffset(localVariableTargetElement.u2startPC);
170 | collectOffset(localVariableTargetElement.u2startPC + localVariableTargetElement.u2length);
171 | }
172 |
173 |
174 | // Implementations for ExceptionInfoVisitor.
175 |
176 | public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
177 | {
178 | collectOffset(exceptionInfo.u2startPC);
179 | collectOffset(exceptionInfo.u2endPC);
180 | // Exception handlers are handled inline.
181 | }
182 |
183 |
184 | // Small utility methods.
185 |
186 | /**
187 | * Adds an offset to the labels map.
188 | *
189 | * @param offset the offset to add.
190 | */
191 | private void collectOffset(int offset)
192 | {
193 | if (!this.labels.containsKey(offset))
194 | {
195 | this.labels.put(offset, AssemblyConstants.LABEL + (labels.size() + 1));
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/PrintException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler;
19 |
20 | /**
21 | * General purpose printing exception.
22 | *
23 | * @author Joachim Vandersmissen
24 | */
25 | public class PrintException extends RuntimeException
26 | {
27 | /**
28 | * Constructs a new PrintException with a message.
29 | *
30 | * @param message the message to add to the exception.
31 | */
32 | public PrintException(String message)
33 | {
34 | super(message);
35 | }
36 |
37 |
38 | /**
39 | * Constructs a new PrintException with a message and the cause of the
40 | * exception.
41 | *
42 | * @param message the message to add to the exception.
43 | * @param cause the cause of the exception.
44 | */
45 | public PrintException(String message, Throwable cause)
46 | {
47 | super(message, cause);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/Printer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler;
19 |
20 | import com.guardsquare.proguard.assembler.AssemblyConstants;
21 | import proguard.classfile.*;
22 | import proguard.classfile.util.ClassUtil;
23 |
24 | import java.io.*;
25 | import java.util.Map;
26 |
27 | /**
28 | * General purpose printer.
29 | *
30 | * @author Joachim Vandersmissen
31 | */
32 | public class Printer
33 | {
34 | private static final String INDENTATION = " ";
35 |
36 | private final Writer w;
37 |
38 | private int indentation;
39 |
40 | public Map labels;
41 |
42 |
43 | /**
44 | * Creates a new Printer that writes to the given writer.
45 | *
46 | * @param writer the writer to write to.
47 | */
48 | public Printer(Writer writer)
49 | {
50 | this.w = writer;
51 | }
52 |
53 |
54 | /**
55 | * Increases the level of indentation by one.
56 | */
57 | public void indent()
58 | {
59 | indentation++;
60 | }
61 |
62 |
63 | /**
64 | * Decreases the level of indentation by one.
65 | */
66 | public void outdent()
67 | {
68 | indentation--;
69 | }
70 |
71 |
72 | /**
73 | * Writes a character to the underlying writer.
74 | *
75 | * @param c the character to write.
76 | * @throws PrintException if an IOException occured while writing.
77 | */
78 | private void write(int c)
79 | {
80 | try
81 | {
82 | w.write(c);
83 | }
84 | catch (IOException e)
85 | {
86 | throw new PrintException("An IOException occured while writing.", e);
87 | }
88 | }
89 |
90 |
91 | /**
92 | * Writes a string to the underlying writer.
93 | *
94 | * @param s the string to write.
95 | * @throws PrintException if an IOException occured while writing.
96 | */
97 | private void write(String s)
98 | {
99 | try
100 | {
101 | w.write(s);
102 | }
103 | catch (IOException e)
104 | {
105 | throw new PrintException("An IOException occured while writing.", e);
106 | }
107 | }
108 |
109 |
110 | /**
111 | * Writes the system line separator.
112 | */
113 | public void println()
114 | {
115 | write(System.lineSeparator());
116 | }
117 |
118 |
119 | /**
120 | * Writes the indentation.
121 | */
122 | public void printIndent()
123 | {
124 | for (int index = 0; index < indentation; index++)
125 | {
126 | write(INDENTATION);
127 | }
128 | }
129 |
130 |
131 | /**
132 | * Writes a single character.
133 | *
134 | * @param c The character to write.
135 | */
136 | public void print(char c)
137 | {
138 | write(c);
139 | }
140 |
141 |
142 | /**
143 | * Writes a single space.
144 | */
145 | public void printSpace()
146 | {
147 | write(' ');
148 | }
149 |
150 |
151 | /**
152 | * Writes a number.
153 | * The number will be formatted according to the DecimalFormat field.
154 | *
155 | * @param number the number to write.
156 | */
157 | public void printNumber(double number)
158 | {
159 | write(AssemblyConstants.DOUBLE_TO_STRING.format(number));
160 | }
161 |
162 |
163 | /**
164 | * Writes a word.
165 | *
166 | * @param word the word to write.
167 | */
168 | public void printWord(String word)
169 | {
170 | write(word);
171 | }
172 |
173 |
174 | /**
175 | * Writes a string, surrounded by double quotes.
176 | *
177 | * @param string the string value.
178 | */
179 | public void printString(String string)
180 | {
181 | write(AssemblyConstants.STRING_QUOTE);
182 | StringBuilder stringBuilder = new StringBuilder();
183 | for (char c : string.toCharArray())
184 | {
185 | if (c < ' ' || c > '~')
186 | {
187 | stringBuilder.append("\\" + Integer.toOctalString(c));
188 | }
189 | else if (c == '"')
190 | {
191 | stringBuilder.append("\\\"");
192 | }
193 | else if (c == '\\')
194 | {
195 | stringBuilder.append("\\\\");
196 | }
197 | else
198 | {
199 | stringBuilder.append(c);
200 | }
201 | }
202 |
203 | write(stringBuilder.toString());
204 | write(AssemblyConstants.STRING_QUOTE);
205 | }
206 |
207 |
208 | /**
209 | * Writes a char, surrounded by single quotes.
210 | *
211 | * @param c the char value.
212 | */
213 | public void printChar(char c)
214 | {
215 | write(AssemblyConstants.CHAR_QUOTE);
216 | if (c < ' ' || c > '~')
217 | {
218 | write("\\" + Integer.toOctalString(c));
219 | }
220 | else if (c == '\'')
221 | {
222 | write("\\'");
223 | }
224 | else if (c == '\\')
225 | {
226 | write("\\\\");
227 | }
228 | else
229 | {
230 | write(c);
231 | }
232 |
233 | write(AssemblyConstants.CHAR_QUOTE);
234 | }
235 |
236 |
237 | /**
238 | * Writes an internal field type.
239 | *
240 | * @param type the internal field type.
241 | */
242 | public void printType(String type)
243 | {
244 | printWord(ClassUtil.externalType(type));
245 | }
246 |
247 |
248 | /**
249 | * Writes a method return type.
250 | *
251 | * @param methodDescriptor The internal method descriptor containing the
252 | * method return type and the method arguments.
253 | */
254 | public void printMethodReturnType(String methodDescriptor)
255 | {
256 | printType(ClassUtil.internalMethodReturnType(methodDescriptor));
257 | }
258 |
259 |
260 | /**
261 | * Writes method arguments.
262 | *
263 | * @param methodDescriptor The internal method descriptor containing the
264 | * method return type and the method arguments.
265 | */
266 | public void printMethodArguments(String methodDescriptor)
267 | {
268 | print(JavaTypeConstants.METHOD_ARGUMENTS_OPEN);
269 | printWord(ClassUtil.externalMethodArguments(methodDescriptor));
270 | print(JavaTypeConstants.METHOD_ARGUMENTS_CLOSE);
271 | }
272 |
273 |
274 | /**
275 | * Writes MethodParameters, RequiresInfo, ExportsInfo, and OpensInfo access
276 | * flags.
277 | *
278 | * @param accessFlags the access flags.
279 | * @return true if at least one access flag was written, false otherwise.
280 | */
281 | public boolean printAccessFlags(int accessFlags)
282 | {
283 | if (accessFlags == 0)
284 | {
285 | return false;
286 | }
287 |
288 | StringBuilder stringBuilder = new StringBuilder();
289 | if ((accessFlags & AccessConstants.FINAL) != 0) stringBuilder.append(JavaAccessConstants.FINAL).append(' ');
290 | if ((accessFlags & AccessConstants.TRANSITIVE) != 0) stringBuilder.append(JavaAccessConstants.TRANSITIVE).append(' ');
291 | if ((accessFlags & AccessConstants.STATIC_PHASE) != 0) stringBuilder.append(JavaAccessConstants.STATIC_PHASE).append(' ');
292 | if ((accessFlags & AccessConstants.SYNTHETIC) != 0) stringBuilder.append(JavaAccessConstants.SYNTHETIC).append(' ');
293 | if ((accessFlags & AccessConstants.MANDATED) != 0) stringBuilder.append(JavaAccessConstants.MANDATED).append(' ');
294 | printWord(stringBuilder.toString().trim());
295 | return true;
296 | }
297 |
298 |
299 | /**
300 | * Writes ProgramClass and InnerClassInfo access flags and class types.
301 | *
302 | * @param accessFlags the access flags.
303 | * @return true if at least one access flag or class type was written, false
304 | * otherwise.
305 | */
306 | public boolean printClassAccessFlags(int accessFlags)
307 | {
308 | StringBuilder stringBuilder = new StringBuilder();
309 | if ((accessFlags & AccessConstants.PUBLIC) != 0) stringBuilder.append(JavaAccessConstants.PUBLIC).append(' ');
310 | if ((accessFlags & AccessConstants.PRIVATE) != 0) stringBuilder.append(JavaAccessConstants.PRIVATE).append(' ');
311 | if ((accessFlags & AccessConstants.PROTECTED) != 0) stringBuilder.append(JavaAccessConstants.PROTECTED).append(' ');
312 | if ((accessFlags & AccessConstants.STATIC) != 0) stringBuilder.append(JavaAccessConstants.STATIC).append(' ');
313 | if ((accessFlags & AccessConstants.FINAL) != 0) stringBuilder.append(JavaAccessConstants.FINAL).append(' ');
314 | if ((accessFlags & AccessConstants.ABSTRACT) != 0) stringBuilder.append(JavaAccessConstants.ABSTRACT).append(' ');
315 | if ((accessFlags & AccessConstants.SYNTHETIC) != 0) stringBuilder.append(JavaAccessConstants.SYNTHETIC).append(' ');
316 |
317 | if ((accessFlags & AccessConstants.MODULE) != 0)
318 | {
319 | stringBuilder.append(JavaAccessConstants.MODULE);
320 | printWord(stringBuilder.toString());
321 | return true;
322 | }
323 |
324 | if ((accessFlags & AccessConstants.ENUM) != 0)
325 | {
326 | stringBuilder.append(JavaAccessConstants.ENUM);
327 | printWord(stringBuilder.toString());
328 | return true;
329 | }
330 |
331 | if ((accessFlags & AccessConstants.ANNOTATION) != 0)
332 | {
333 | stringBuilder.append(JavaAccessConstants.ANNOTATION).append(JavaAccessConstants.INTERFACE);
334 | printWord(stringBuilder.toString());
335 | return true;
336 | }
337 |
338 | if ((accessFlags & AccessConstants.INTERFACE) != 0)
339 | {
340 | stringBuilder.append(JavaAccessConstants.INTERFACE);
341 | printWord(stringBuilder.toString());
342 | return true;
343 | }
344 |
345 | stringBuilder.append(AssemblyConstants.CLASS);
346 | printWord(stringBuilder.toString());
347 | return true;
348 | }
349 |
350 |
351 | /**
352 | * Writes a bytecode offset.
353 | *
354 | * @param offset the bytecode offset.
355 | * @throws PrintException if the offset was not found in the labels map.
356 | */
357 | public void printOffset(int offset)
358 | {
359 | if (!labels.containsKey(offset))
360 | {
361 | throw new PrintException("Offset " + offset + " not found in labels.");
362 | }
363 |
364 | printWord(labels.get(offset));
365 | }
366 |
367 |
368 | /**
369 | * Flushes the writer.
370 | */
371 | public void flush()
372 | {
373 | try
374 | {
375 | w.flush();
376 | }
377 | catch (IOException ignore) {}
378 | }
379 | }
380 |
--------------------------------------------------------------------------------
/pga-lib/src/main/java/com/guardsquare/proguard/disassembler/io/JbcDataEntryWriter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ProGuard assembler/disassembler for Java bytecode.
3 | *
4 | * Copyright (c) 2019-2020 Guardsquare NV
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package com.guardsquare.proguard.disassembler.io;
19 |
20 | import com.guardsquare.proguard.assembler.AssemblyConstants;
21 | import com.guardsquare.proguard.disassembler.ClassPrinter;
22 | import com.guardsquare.proguard.disassembler.Printer;
23 | import proguard.classfile.ClassPool;
24 | import proguard.classfile.Clazz;
25 | import proguard.io.DataEntry;
26 | import proguard.io.DataEntryWriter;
27 |
28 | import java.io.FilterOutputStream;
29 | import java.io.IOException;
30 | import java.io.OutputStream;
31 | import java.io.OutputStreamWriter;
32 | import java.io.PrintWriter;
33 | import java.nio.charset.StandardCharsets;
34 |
35 | /**
36 | * This DataEntryWriter finds received class entries in the given class pool
37 | * and disassembles them as jbc files to the given data entry writer. For
38 | * resource entries, it returns valid output streams. For jbc entries, it
39 | * returns output streams that must not be used.
40 | *
41 | * @author Joachim Vandersmissen
42 | */
43 | public class JbcDataEntryWriter
44 | implements DataEntryWriter
45 | {
46 | private final ClassPool classPool;
47 | private final DataEntryWriter dataEntryWriter;
48 |
49 |
50 | /**
51 | * Creates a new JbcDataEntryWriter.
52 | * @param classPool the class pool in which classes are found.
53 | * @param dataEntryWriter the writer to which the jbc file is disassembled.
54 | */
55 | public JbcDataEntryWriter(ClassPool classPool,
56 | DataEntryWriter dataEntryWriter)
57 | {
58 | this.classPool = classPool;
59 | this.dataEntryWriter = dataEntryWriter;
60 | }
61 |
62 |
63 | // Implementations for DataEntryWriter.
64 |
65 | public boolean createDirectory(DataEntry dataEntry) throws IOException
66 | {
67 | return dataEntryWriter.createDirectory(dataEntry);
68 | }
69 |
70 |
71 | public boolean sameOutputStream(DataEntry dataEntry1,
72 | DataEntry dataEntry2)
73 | throws IOException
74 | {
75 | return dataEntryWriter.sameOutputStream(dataEntry1, dataEntry2);
76 | }
77 |
78 |
79 | public OutputStream createOutputStream(DataEntry dataEntry) throws IOException
80 | {
81 | // Is it a class entry?
82 | String name = dataEntry.getName();
83 | if (name.endsWith(AssemblyConstants.JBC_EXTENSION))
84 | {
85 | // Does it still have a corresponding class?
86 | String className = name.substring(0, name.length() - AssemblyConstants.JBC_EXTENSION.length());
87 | Clazz clazz = classPool.getClass(className);
88 | if (clazz != null)
89 | {
90 | // Get the output stream for this input entry.
91 | OutputStream outputStream = dataEntryWriter.createOutputStream(dataEntry);
92 | if (outputStream != null)
93 | {
94 | // Disassemble the class to the output stream.
95 | try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))
96 | {
97 | clazz.accept(new ClassPrinter(new Printer(outputStreamWriter)));
98 | }
99 | catch (Exception e)
100 | {
101 | throw new IOException("An exception occured while disassembling " + dataEntry.getName(), e);
102 | }
103 | }
104 | }
105 |
106 | // Return a dummy, non-null output stream (to work with cascading
107 | // output writers).
108 | return new FilterOutputStream(null);
109 | }
110 |
111 | // Delegate for resource entries.
112 | return dataEntryWriter.createOutputStream(dataEntry);
113 | }
114 |
115 |
116 | public void close() throws IOException
117 | {
118 | // Close the delegate writer.
119 | dataEntryWriter.close();
120 | }
121 |
122 |
123 | public void println(PrintWriter pw, String prefix)
124 | {
125 | pw.println(prefix + "JbcDataEntryWriter");
126 | dataEntryWriter.println(pw, prefix + " ");
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/pga-lib/src/test/kotlin/com/guardsquare/proguard/AssemblerErrorsTest.kt:
--------------------------------------------------------------------------------
1 | package com.guardsquare.proguard
2 |
3 | import com.guardsquare.proguard.assembler.ClassParser
4 | import com.guardsquare.proguard.assembler.ParseException
5 | import com.guardsquare.proguard.assembler.Parser
6 | import io.kotest.assertions.throwables.shouldThrow
7 | import io.kotest.core.spec.style.FreeSpec
8 | import proguard.classfile.ProgramClass
9 | import java.io.StringReader
10 |
11 | /**
12 | * These tests check that various invalid code snippets are causing in an exceptions thrown from the
13 | * assembler.
14 | */
15 | class AssemblerErrorsTest : FreeSpec({
16 |
17 | "Variable types on the stack do not match instruction using them" {
18 | shouldThrow {
19 | buildProgramClass(
20 | """
21 | public class AssemblerErrorsTester extends java.lang.Object {
22 | public long test() {
23 | iconst_3
24 | iconst_1
25 | lsub
26 | lreturn
27 | }
28 | }
29 | """,
30 | )
31 | }
32 | }
33 |
34 | "Swap needs two elements on the stack" {
35 | shouldThrow {
36 | buildProgramClass(
37 | """
38 | public class AssemblerErrorsTester extends java.lang.Object {
39 | public void test() {
40 | iconst_5
41 | swap
42 | return
43 | }
44 | }
45 | """,
46 | )
47 | }
48 | }
49 |
50 | "Duplicate top value of an empty stack" {
51 | shouldThrow {
52 | buildProgramClass(
53 | """
54 | public class AssemblerErrorsTester extends java.lang.Object {
55 | public void test() {
56 | dup
57 | return
58 | }
59 | }
60 | """,
61 | )
62 | }
63 | }
64 |
65 | "Illegal bytecode instruction" {
66 | // `apples` is not a valid bytecode instructions and this should be clearly indicated by the PGA
67 | // See: https://github.com/Guardsquare/proguard-assembler/issues/8
68 | shouldThrow {
69 | buildProgramClass(
70 | """
71 | public class AssemblerErrorsTester extends java.lang.Object {
72 | public java.lang.Object test() {
73 | apples
74 | aload_0
75 | areturn
76 | }
77 | }
78 | """,
79 | )
80 | }
81 | }
82 |
83 | "`goto` to an invalid position" {
84 | shouldThrow {
85 | buildProgramClass(
86 | """
87 | public class AssemblerErrorsTester extends java.lang.Object {
88 | public void test() {
89 | goto jafar
90 | return
91 | }
92 | }
93 | """,
94 | )
95 | }
96 | }
97 |
98 | "bipush with invalid operand label" {
99 | // `bipush` expects a byte value but 300 exceeds the maximum byte value (>255)
100 | // If you want to fix this, you need to be in visitCodeAttribute:226 in the instructionParser (of the assembler)
101 | // Additionally you change the error to hande null for the clazz and method. (Just write something like "non-existing" or "null")
102 | shouldThrow {
103 | buildProgramClass(
104 | """
105 | public class AssemblerErrorsTester extends java.lang.Object {
106 | public java.lang.Object test() {
107 | bipush 300
108 | aload_0
109 | areturn
110 | }
111 | }
112 | """,
113 | )
114 | }
115 | }
116 | })
117 |
118 | /**
119 | * Helper function to build and parse a java bytecode class from a string
120 | */
121 | fun buildProgramClass(jbc: String) {
122 | val programClass = ProgramClass()
123 | programClass.accept(
124 | ClassParser(
125 | Parser(
126 | StringReader(jbc),
127 | ),
128 | ),
129 | )
130 | }
131 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 |
2 | plugins {
3 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
4 | }
5 |
6 |
7 | rootProject.name = 'proguard-assembler'
8 | include 'pga-lib'
9 | include 'pga-cli'
10 |
--------------------------------------------------------------------------------