diff --git a/src/main/java/org/eolang/opeo/SelectiveDecompiler.java b/src/main/java/org/eolang/opeo/SelectiveDecompiler.java index 931e67da..35f8c865 100644 --- a/src/main/java/org/eolang/opeo/SelectiveDecompiler.java +++ b/src/main/java/org/eolang/opeo/SelectiveDecompiler.java @@ -1,8 +1,15 @@ package org.eolang.opeo; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.eolang.opeo.ast.OpcodeName; import org.eolang.opeo.decompilation.Decompiler; +import org.eolang.opeo.decompilation.handlers.RouterHandler; +import org.eolang.opeo.jeo.JeoDecompiler; import org.eolang.opeo.storage.Storage; +import org.eolang.opeo.storage.XmirEntry; /** * Selective decompiler. @@ -14,18 +21,57 @@ */ public final class SelectiveDecompiler implements Decompiler { - /** * The storage where the XMIRs are stored. */ private final Storage storage; + /** + * Where to save the copy of each decompiled file. + */ + private final Storage copy; + + + private final String[] supported; + public SelectiveDecompiler(final Storage storage) { + this(storage, new RouterHandler(true).supportedOpcodes()); + } + + public SelectiveDecompiler(final Storage storage, String... supported) { + this(storage, storage, supported); + } + + public SelectiveDecompiler( + final Storage storage, final Storage copy, final String... supported + ) { this.storage = storage; + this.copy = copy; + this.supported = supported; } @Override public void decompile() { + this.storage.all().forEach( + entry -> { + final XmirEntry res; + final List xpath = entry.xpath(this.xpath()); + final boolean empty = xpath.isEmpty(); + if (empty) { + res = entry.transform(xml -> new JeoDecompiler(xml).decompile()); + } else { + res = entry; + } + this.copy.save(res); + this.storage.save(res); + } + ); + } + private String xpath() { + return String.format( + "//o[@base='opcode'][not(contains(' %s ', concat(' ', @name, ' '))) ]/@name", + Arrays.stream(this.supported).collect(Collectors.joining(" ")) + ); } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/RouterHandler.java b/src/main/java/org/eolang/opeo/decompilation/handlers/RouterHandler.java index d73470c1..543e4795 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/RouterHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/handlers/RouterHandler.java @@ -27,6 +27,7 @@ import org.cactoos.map.MapEntry; import org.cactoos.map.MapOf; import org.eolang.opeo.ast.Opcode; +import org.eolang.opeo.ast.OpcodeName; import org.eolang.opeo.decompilation.DecompilerState; import org.eolang.opeo.decompilation.InstructionHandler; import org.eolang.opeo.jeo.JeoLabel; @@ -141,6 +142,18 @@ public void handle(final DecompilerState state) { this.handler(state.instruction().opcode()).handle(state); } + /** + * Get supported opcodes. + * @return Supported opcodes. + */ + public String[] supportedOpcodes() { + return this.handlers.keySet() + .stream() + .map(OpcodeName::new) + .map(OpcodeName::simplified) + .toArray(String[]::new); + } + /** * Get instruction handler. * @param opcode Instruction opcode. diff --git a/src/main/java/org/eolang/opeo/storage/XmirEntry.java b/src/main/java/org/eolang/opeo/storage/XmirEntry.java index e0ada73d..5beba406 100644 --- a/src/main/java/org/eolang/opeo/storage/XmirEntry.java +++ b/src/main/java/org/eolang/opeo/storage/XmirEntry.java @@ -27,6 +27,7 @@ import com.jcabi.xml.XMLDocument; import java.io.FileNotFoundException; import java.nio.file.Path; +import java.util.List; import java.util.function.Function; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -89,6 +90,15 @@ public XmirEntry transform(final Function transforme return new XmirEntry(transformer.apply(this.xml.value()), this.pckg); } + /** + * Apply XPath query. + * @param query XPath query. + * @return List of strings returned by query. + */ + public List xpath(final String query) { + return this.xml.value().xpath(query); + } + /** * To XML. * @return XML representation of XMIR. diff --git a/src/test/java/org/eolang/opeo/decompilation/SelectiveDecompilerTest.java b/src/test/java/org/eolang/opeo/decompilation/SelectiveDecompilerTest.java index 7294b67f..b98880dc 100644 --- a/src/test/java/org/eolang/opeo/decompilation/SelectiveDecompilerTest.java +++ b/src/test/java/org/eolang/opeo/decompilation/SelectiveDecompilerTest.java @@ -23,12 +23,14 @@ */ package org.eolang.opeo.decompilation; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; +import java.util.stream.Stream; import org.cactoos.bytes.BytesOf; import org.cactoos.io.ResourceOf; import org.eolang.opeo.SelectiveDecompiler; +import org.eolang.opeo.decompilation.handlers.RouterHandler; import org.eolang.opeo.storage.FileStorage; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -42,6 +44,12 @@ */ final class SelectiveDecompilerTest { + private final String[] supported = + Stream.concat( + Arrays.stream(new RouterHandler(false).supportedOpcodes()), + Stream.of("IRETURN", "IFLE") + ).toArray(String[]::new); + @Test void decompiles(@TempDir final Path temp) throws Exception { final byte[] known = new BytesOf(new ResourceOf("xmir/Bar.xmir")).asBytes(); @@ -50,7 +58,7 @@ void decompiles(@TempDir final Path temp) throws Exception { Files.createDirectories(input); Files.createDirectories(output); Files.write(input.resolve("Bar.xmir"), known); - new SelectiveDecompiler(new FileStorage(input, output)).decompile(); + new SelectiveDecompiler(new FileStorage(input, output), this.supported).decompile(); MatcherAssert.assertThat( "We expect that decompiler will decompile the input file and store the result into the output folder.", output.resolve("Bar.xmir").toFile(), @@ -58,8 +66,8 @@ void decompiles(@TempDir final Path temp) throws Exception { ); MatcherAssert.assertThat( "We expect that the decompiled file won't be the same as the input file. Since the decompiler should change the file.", - new BytesOf(output.resolve("Bar.xmir")), - Matchers.not(Matchers.equalTo(new BytesOf(known))) + new BytesOf(output.resolve("Bar.xmir")).asBytes(), + Matchers.not(Matchers.equalTo(known)) ); } @@ -79,8 +87,8 @@ void decompilesNothing(@TempDir final Path temp) throws Exception { ); MatcherAssert.assertThat( "We expect that the decompiled file will be the same as the input file. Since the decompiler doesn't know some instructions.", - new BytesOf(output.resolve("Bar.xmir")), - Matchers.equalTo(new BytesOf(known)) + new BytesOf(output.resolve("Bar.xmir")).asBytes(), + Matchers.equalTo(known) ); } @@ -97,13 +105,13 @@ void decompilesOnlySomeFiles(@TempDir final Path temp) throws Exception { new SelectiveDecompiler(new FileStorage(input, output)).decompile(); MatcherAssert.assertThat( "We expect that the decompiled file will be changed since the decompiler knows all instructions.", - new BytesOf(output.resolve("Known.xmir")), - Matchers.not(Matchers.equalTo(new BytesOf(known))) + new BytesOf(output.resolve("Known.xmir")).asBytes(), + Matchers.not(Matchers.equalTo(known)) ); MatcherAssert.assertThat( "We expect that the decompiled file won't be changed since the decompiler doesn't know some instructions.", - new BytesOf(output.resolve("Unknown.xmir")), - Matchers.equalTo(new BytesOf(unknown)) + new BytesOf(output.resolve("Unknown.xmir")).asBytes(), + Matchers.equalTo(unknown) ); }