-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathDisassembleMojo.java
More file actions
284 lines (268 loc) · 9.63 KB
/
DisassembleMojo.java
File metadata and controls
284 lines (268 loc) · 9.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang.jeo;
import com.jcabi.log.Logger;
import java.io.File;
import java.nio.file.Path;
import java.util.Set;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.cactoos.set.SetOf;
import org.eolang.jeo.representation.directives.Format;
/**
* Disassembles Java bytecode into XMIR representation.
*
* <p>This Maven plugin converts compiled Java class files into low-level EO representation
* (in XMIR format) that contains JVM opcodes and their operands. The resulting XMIR files
* preserve all bytecode instructions and can be assembled back into executable class files.</p>
*
* <p>The plugin supports different disassembly modes to control the level of detail in the
* output, including debug information such as line numbers and variable names.</p>
*
* @since 0.1.0
*/
@Mojo(name = "disassemble", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresProject = false)
public final class DisassembleMojo extends AbstractMojo {
/**
* Maven project instance.
* <p>
* Provides access to project configuration and classpath dependencies required for
* bytecode analysis and disassembly.
* </p>
*
* @since 0.2.0
*/
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;
/**
* Source directory containing compiled Java class files.
* <p>
* This directory should contain {@code .class} files that will be disassembled into
* XMIR format. Typically points to the project's build output directory.
* </p>
*
* @since 0.2.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.sourcesDir",
defaultValue = "${project.build.outputDirectory}"
)
private File sourcesDir;
/**
* Target directory for generated XMIR files.
* <p>
* All disassembled XMIR files will be written to this directory, preserving the package
* structure of the original class files. Each class file will be converted to a corresponding
* XMIR file with {@code .xmir} extension.
* </p>
*
* @since 0.2.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.outputDir",
defaultValue = "${project.build.directory}/generated-sources/jeo-xmir"
)
private File outputDir;
/**
* Flag to disable the plugin execution.
* <p>
* When set to {@code true}, the plugin will skip all processing and exit immediately.
* This can be useful for conditional builds or troubleshooting.
* </p>
*
* @since 0.2.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.disabled",
defaultValue = "false"
)
private boolean disabled;
/**
* Disassembly mode controlling the level of detail in output.
* <p>
* Supported modes:
* <ul>
* <li>{@code short} - Minimal output with bytecode instructions only</li>
* <li>{@code debug} - Include debug information such as line numbers, local variables,
* and source file references (default)</li>
* </ul>
* </p>
*
* @since 0.6.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.mode",
defaultValue = "debug"
)
private String mode;
/**
* Flag to omit detailed bytecode listings in generated XMIR.
* <p>
* When enabled, the {@code <listing>} element in XMIR files will not contain bytecode listing.
* This reduces file size and improves readability in production environments where detailed
* bytecode output is not needed. When disabled, full bytecode listings are included
* for debugging purposes.
* </p>
*
* @since 0.11.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.omitListings",
defaultValue = "true"
)
private boolean omitListings;
/**
* Flag to omit XML comments in generated XMIR files.
* <p>
* When enabled, no comments will be generated in the XMIR output, which can be
* useful for production builds where comments are not needed and may reduce file size.
* When disabled, XML comments will be included to provide debugging information.
* </p>
*
* @since 0.11.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.omitComments",
defaultValue = "true"
)
private boolean omitComments;
/**
* Flag to enable pretty-printing of XMIR files.
* <p>
* When enabled, the generated XMIR files will be formatted with indentation (2 spaces) and
* line breaks for better readability.
* This is useful for development and debugging purposes.
* By default, pretty-printing is enabled, but it's best to disable it for large
* projects or production builds to reduce file size and improve performance.
* </p>
* @since 0.11.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.prettyXmir",
defaultValue = "true"
)
private boolean prettyXmir;
/**
* Flag to enable XMIR verification after disassembling.
* <p>
* When enabled, verifies all generated XMIR files for structural integrity and correctness
* after disassembly. If any XMIR file is invalid or corrupted, the build process will fail.
* This verification is disabled by default for performance.
* </p>
*
* @since 0.8.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.xmir.verification",
defaultValue = "false"
)
private boolean xmirVerification;
/**
* Should method modifiers be included in the output.
* <p>
* When true, method modifiers (e.g., public, private, static) will be
* included in the disassembled output.
* </p>
*
* @since 0.14.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(
property = "jeo.disassemble.xmir.modifiers",
defaultValue = "false"
)
private boolean modifiers;
/**
* Set of inclusion GLOB filters for finding .class files
* in the {@link #sourcesDir} directory.
*
* @since 0.13.0
* @checkstyle MemberNameCheck (15 lines)
*/
@Parameter(property = "jeo.disassemble.includes")
@SuppressWarnings("PMD.ImmutableField")
private Set<String> includes = new SetOf<>("**/*.class");
/**
* Set of exclusion GLOB filters for finding .class files
* in the {@link #sourcesDir} directory.
*
* @since 0.13.0
* @checkstyle MemberNameCheck (7 lines)
*/
@Parameter(property = "jeo.disassemble.excludes")
@SuppressWarnings("PMD.ImmutableField")
private Set<String> excludes = new SetOf<>();
/**
* Enable debug logging for the disassembly process.
* @since 0.15.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(property = "jeo.disassemble.debug", defaultValue = "false")
private boolean debug;
@Override
public void execute() throws MojoExecutionException {
final Path src = new MavenPath(this.sourcesDir).resolve();
final Path out = new MavenPath(this.outputDir).resolve();
try {
new PluginStartup(this.project, src).init();
if (this.disabled) {
Logger.info(this, "Disassemble mojo is disabled, skipping");
} else {
final boolean listings = !this.omitListings;
final boolean comments = !this.omitComments;
Logger.info(
this,
"Disassembling is started with mode '%s' (with listings = '%b', comments = '%b', modifiers = '%b', pretty = '%b')",
this.mode,
listings,
comments,
this.modifiers,
this.prettyXmir
);
new Disassembler(
new FilteredClasses(
new BytecodeClasses(src),
new GlobFilter(this.includes, this.excludes)
),
out,
new Format(
Format.MODIFIERS, this.modifiers,
Format.COMMENTS, comments,
Format.WITH_LISTING, listings,
Format.PRETTY, this.prettyXmir,
Format.MODE, this.mode
),
this.debug
).disassemble();
if (this.xmirVerification) {
Logger.info(this, "Verifying all the XMIR files after disassembling");
new XmirFiles(out).verify();
} else {
Logger.info(
this, "XMIR verification after disassembling is disabled, skipping"
);
}
}
} catch (final DependencyResolutionRequiredException exception) {
throw new MojoExecutionException(
String.format("Failed to transpile bytecode to EO, from '%s' to '%s'", src, out),
exception
);
}
}
}