Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#226): Decompile Only Files With Supported Opcodes" #244

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ SOFTWARE.
</distributionManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<skipITs/>
</properties>
<dependencies>
Expand Down
1 change: 1 addition & 0 deletions src/it/fuse/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ SOFTWARE.
<configuration>
<sourcesDir>${project.build.directory}/generated-sources/jeo-decompile-xmir</sourcesDir>
<outputDir>${project.build.directory}/generated-sources/opeo-decompile-xmir</outputDir>
<modifiedDir>${project.build.directory}/generated-sources/opep-decompile-xmir-modified</modifiedDir>
</configuration>
</execution>
<execution>
Expand Down
1 change: 1 addition & 0 deletions src/it/staticize/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ SOFTWARE.
<configuration>
<sourcesDir>${project.build.directory}/generated-sources/jeo-decompile-xmir</sourcesDir>
<outputDir>${project.build.directory}/generated-sources/opeo-decompile-xmir</outputDir>
<modifiedDir>${project.build.directory}/generated-sources/opep-decompile-xmir-modified</modifiedDir>
</configuration>
</execution>
<execution>
Expand Down
25 changes: 23 additions & 2 deletions src/main/java/org/eolang/opeo/DecompileMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@

import com.jcabi.log.Logger;
import java.io.File;
import java.util.Objects;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.eolang.opeo.decompilation.Decompiler;
import org.eolang.opeo.decompilation.DefaultDecompiler;
import org.eolang.opeo.decompilation.DummyDecompiler;
import org.eolang.opeo.decompilation.NaiveDecompiler;

/**
* Decompiles bytecode in EO representation into high-level EO representation.
Expand Down Expand Up @@ -69,6 +70,20 @@ public final class DecompileMojo extends AbstractMojo {
)
private File outputDir;

/**
* Directory where modified XMIRs are stored.
* It is an optional folder that is used to separate files that were modified.
* In some cases, the decompilation phase might just skip some files because some instructions
* are not supported yet.
* To "see" what we actually decompiled, we store the modified files in this folder.
* It doesn't affect {@link #outputDir}.
*
* @since 0.2.0
* @checkstyle MemberNameCheck (6 lines)
*/
@Parameter(property = "opeo.decompile.modifiedDir")
private File modifiedDir;

/**
* Whether the plugin is disabled.
* If it's disabled, then it won't do anything.
Expand All @@ -88,8 +103,14 @@ public void execute() {
if (this.disabled) {
Logger.info(this, "Decompiler is disabled");
decompiler = new DummyDecompiler(this.sourcesDir.toPath(), this.outputDir.toPath());
} else if (Objects.nonNull(this.modifiedDir)) {
Logger.info(this, "Use selective decompiler");
decompiler = new SelectiveDecompiler(
this.sourcesDir.toPath(), this.outputDir.toPath(), this.modifiedDir.toPath()
);
} else {
decompiler = new DefaultDecompiler(this.sourcesDir.toPath(), this.outputDir.toPath());
Logger.info(this, "Use naive decompiler");
decompiler = new NaiveDecompiler(this.sourcesDir.toPath(), this.outputDir.toPath());
}
decompiler.decompile();
}
Expand Down
168 changes: 168 additions & 0 deletions src/main/java/org/eolang/opeo/SelectiveDecompiler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2023 Objectionary.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.eolang.opeo;

import com.jcabi.log.Logger;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.eolang.opeo.decompilation.Decompiler;
import org.eolang.opeo.decompilation.handlers.RouterHandler;
import org.eolang.opeo.jeo.JeoDecompiler;
import org.eolang.opeo.storage.DummyStorage;
import org.eolang.opeo.storage.FileStorage;
import org.eolang.opeo.storage.Storage;
import org.eolang.opeo.storage.XmirEntry;

/**
* Selective decompiler.
* Decompiler that decompiles ONLY fully understandable methods.
* These methods contain only instructions that are
* supported by {@link org.eolang.opeo.decompilation.handlers.RouterHandler}.
*
* @since 0.1
*/
public final class SelectiveDecompiler implements Decompiler {

/**
* The storage where the XMIRs are stored.
*/
private final Storage storage;

/**
* Where to save the modified of each decompiled file.
*/
private final Storage modified;

/**
* Supported opcodes.
*/
private final String[] supported;

/**
* Constructor.
* @param input Input folder with XMIRs.
* @param output Output folder where to save the decompiled files.
* @param modified Folder where to save the modified XMIRs.
*/
public SelectiveDecompiler(final Path input, final Path output, final Path modified) {
this(input, output, modified, new RouterHandler(false).supportedOpcodes());
}

/**
* Constructor.
* @param input Input folder with XMIRs.
* @param output Output folder where to save the decompiled files.
*/
public SelectiveDecompiler(final Path input, final Path output) {
this(input, output, new RouterHandler(false).supportedOpcodes());
}

/**
* Constructor.
* @param input Input folder with XMIRs.
* @param output Output folder where to save the decompiled files.
* @param supported Supported opcodes are used in selection.
*/
public SelectiveDecompiler(
final Path input, final Path output, final String... supported
) {
this(new FileStorage(input, output), supported);
}

/**
* Constructor.
* @param input Input folder with XMIRs.
* @param output Output folder where to save the decompiled files.
* @param modified Folder where to save the modified XMIRs.
* @param supported Supported opcodes are used in selection.
* @checkstyle ParameterNumberCheck (5 lines)
*/
public SelectiveDecompiler(
final Path input,
final Path output,
final Path modified,
final String... supported
) {
this(new FileStorage(input, output), new FileStorage(modified, modified), supported);
}

/**
* Constructor.
* @param storage Storage from which retrieve the XMIRs and where to save the modified ones.
* @param supported Supported opcodes are used in selection.
*/
public SelectiveDecompiler(final Storage storage, final String... supported) {
this(storage, new DummyStorage(), supported);
}

/**
* Constructor.
* @param storage Storage from which retrieve the XMIRs and where to save the modified ones.
* @param modified Storage where to save the modified of each decompiled file.
* @param supported Supported opcodes are used in selection.
*/
public SelectiveDecompiler(
final Storage storage, final Storage modified, final String... supported
) {
this.storage = storage;
this.modified = modified;
this.supported = supported.clone();
}

@Override
public void decompile() {
this.storage.all().forEach(
entry -> {
final XmirEntry res;
final List<String> found = entry.xpath(this.xpath());
if (found.isEmpty()) {
res = entry.transform(xml -> new JeoDecompiler(xml).decompile());
this.modified.save(res);
} else {
Logger.info(
this,
"Skipping %s, because of unsupported opcodes: %s",
entry,
found
);
res = entry;
}
this.storage.save(res);
}
);
}

/**
* Xpath to find all opcodes that are not supported.
* @return Xpath.
*/
private String xpath() {
return String.format(
"//o[@base='opcode'][not(contains('%s', substring-before(concat(@name, '-'), '-'))) ]/@name",
Arrays.stream(this.supported).collect(Collectors.joining(" "))
);
}
}
78 changes: 78 additions & 0 deletions src/main/java/org/eolang/opeo/ast/RawXml.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2023 Objectionary.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.eolang.opeo.ast;

import com.jcabi.xml.XMLDocument;
import java.util.Collections;
import java.util.List;
import org.eolang.jeo.representation.xmir.XmlNode;
import org.xembly.Directive;
import org.xembly.Directives;

/**
* This class represents a raw XML node.
* It is used to represent nodes that we don't know how to parse yet.
*
* @since 0.1
*/
public final class RawXml implements AstNode {

/**
* XML node.
*/
private final XmlNode node;

/**
* Constructor.
* @param node XML node.
*/
public RawXml(final XmlNode node) {
this.node = node;
}

@Override
public List<AstNode> opcodes() {
//@checkstyle MethodBodyCommentsCheck (10 lines)
// @todo #226:90min Refactor AstNode#opcodes() Method Usage.
// This method is used improperly in the codebase since we have such strange
// implementations as in the RawXml class. We need to refactor the codebase
// and maybe just remove this method and use only `toXmir`.
// So, we need to investigate this.
return Collections.singletonList(this);
}

@Override
public Iterable<Directive> toXmir() {
//@checkstyle MethodBodyCommentsCheck (10 line)
// @todo #226:90min Inefficient RawXml#toXmir() Method Implementation.
// This method is inefficient because it creates a new XMLDocument
// instance and then converts it to a string. We need to refactor this
// method to be more efficient.
return new Directives().append(
new XMLDocument(
new XMLDocument(this.node.node()).toString()
).node()
);
}
}
3 changes: 3 additions & 0 deletions src/main/java/org/eolang/opeo/compilation/XmirParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.eolang.opeo.ast.Literal;
import org.eolang.opeo.ast.LocalVariable;
import org.eolang.opeo.ast.Opcode;
import org.eolang.opeo.ast.RawXml;
import org.eolang.opeo.ast.StaticInvocation;
import org.eolang.opeo.ast.StoreArray;
import org.eolang.opeo.ast.Substraction;
Expand Down Expand Up @@ -160,6 +161,8 @@ private AstNode node(final XmlNode node) {
result = new Substraction(node, this::node);
} else if ("cast".equals(base)) {
result = new Cast(node, this::node);
} else if ("frame".equals(base)) {
result = new RawXml(node);
} else if ("opcode".equals(base)) {
final XmlInstruction instruction = new XmlInstruction(node);
final int opcode = instruction.opcode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@
import org.eolang.opeo.storage.XmirEntry;

/**
* Default Decompiler.
* Naive Decompiler.
* This class is a high-level abstraction of the decompilation process.
* The main purpose of this class is to get the output of the jeo-maven-plugin
* and decompile it into high-level EO constructs.
*
* This class just tries to decompile ALL possible files.
*
* @since 0.1
*/
public final class DefaultDecompiler implements Decompiler {
public final class NaiveDecompiler implements Decompiler {

/**
* The storage where the XMIRs are stored.
Expand All @@ -50,7 +52,7 @@ public final class DefaultDecompiler implements Decompiler {
* @param xmirs Path to the generated XMIRs by jeo-maven-plugin.
* @param output Path to the output directory.
*/
public DefaultDecompiler(
public NaiveDecompiler(
final Path xmirs,
final Path output
) {
Expand All @@ -61,15 +63,15 @@ public DefaultDecompiler(
* Constructor.
* @param generated The default Maven 'generated-sources' directory.
*/
DefaultDecompiler(final Path generated) {
NaiveDecompiler(final Path generated) {
this(generated.resolve("xmir"), generated.resolve("opeo-xmir"));
}

/**
* Constructor.
* @param storage The storage where the XMIRs are stored.
*/
private DefaultDecompiler(final Storage storage) {
private NaiveDecompiler(final Storage storage) {
this.storage = storage;
}

Expand Down
Loading
Loading