diff --git a/src/it/optimization/pom.xml b/src/it/optimization/pom.xml index d52a4a59a..e044e492e 100644 --- a/src/it/optimization/pom.xml +++ b/src/it/optimization/pom.xml @@ -28,7 +28,12 @@ SOFTWARE. jeo-it @project.version@ jar - Integration test + Integration test that tries to transform simple java program + into XMIR and back. The integration test also checks that program can run + successfully and the plugin didn't break anything. + If you need to run only this test, use the following command: + "mvn clean integration-test invoker:run -Dinvoker.test=optimization -DskipTests" + UTF-8 1.8 diff --git a/src/it/optimization/src/main/java/org/eolang/jeo/Application.java b/src/it/optimization/src/main/java/org/eolang/jeo/Application.java index 94ed6e37b..873301149 100644 --- a/src/it/optimization/src/main/java/org/eolang/jeo/Application.java +++ b/src/it/optimization/src/main/java/org/eolang/jeo/Application.java @@ -2,6 +2,17 @@ public class Application { public static void main(String[] args) { - System.out.println("Hello, World!"); + if (bar(1.0d) < 7) { + System.out.println("Hello, World!"); + } else { + System.out.println("Wake up, Neo..."); + } + } + + private static int bar(double x) { + if (x > 0.0d) { + return 5; + } + return 8; } } diff --git a/src/main/java/org/eolang/jeo/representation/bytecode/CommandInstruction.java b/src/main/java/org/eolang/jeo/representation/bytecode/CommandInstruction.java index 9d0eaa480..953f789b6 100644 --- a/src/main/java/org/eolang/jeo/representation/bytecode/CommandInstruction.java +++ b/src/main/java/org/eolang/jeo/representation/bytecode/CommandInstruction.java @@ -130,6 +130,13 @@ private enum Instruction { visitor.visitInsn(Opcodes.DCONST_0) ), + /** + * Load the double value 1 onto the stack. + */ + DCONST_1(Opcodes.DCONST_1, (visitor, arguments) -> + visitor.visitInsn(Opcodes.DCONST_1) + ), + /** * Push a byte onto the stack as an integer value. */ @@ -228,6 +235,36 @@ private enum Instruction { ) ), + /** + * If value1 is greater than or equal to value2, branch to instruction at branchoffset. + */ + IF_ICMPGE(Opcodes.IF_ICMPGE, (visitor, arguments) -> + visitor.visitJumpInsn( + Opcodes.IF_ICMPGE, + (org.objectweb.asm.Label) arguments.get(0) + ) + ), + + /** + * If value1 is less than or equal to value2, branch to instruction at branchoffset. + */ + IF_ICMPLE(Opcodes.IF_ICMPLE, (visitor, arguments) -> + visitor.visitJumpInsn( + Opcodes.IF_ICMPLE, + (org.objectweb.asm.Label) arguments.get(0) + ) + ), + + /** + * Goes to another instruction at branchoffset. + */ + GOTO(Opcodes.GOTO, (visitor, arguments) -> + visitor.visitJumpInsn( + Opcodes.GOTO, + (org.objectweb.asm.Label) arguments.get(0) + ) + ), + /** * Return an integer from a method. */ @@ -303,7 +340,7 @@ private enum Instruction { * Invoke instance method on object objectref and puts the result on the stack. * Might be void. The method is identified by method reference index in constant pool. */ - INCOKESPECIAL(Opcodes.INVOKESPECIAL, (visitor, arguments) -> + INVOKESPECIAL(Opcodes.INVOKESPECIAL, (visitor, arguments) -> visitor.visitMethodInsn( Opcodes.INVOKESPECIAL, String.valueOf(arguments.get(0)), @@ -313,6 +350,19 @@ private enum Instruction { ) ), + /** + * Invoke a class (static) method. + */ + INVOKESTATIC(Opcodes.INVOKESTATIC, (visitor, arguments) -> + visitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + String.valueOf(arguments.get(0)), + String.valueOf(arguments.get(1)), + String.valueOf(arguments.get(2)), + false + ) + ), + /** * Create new object of type identified by class reference in constant pool index. */ diff --git a/src/main/java/org/eolang/jeo/representation/xmir/HexString.java b/src/main/java/org/eolang/jeo/representation/xmir/HexString.java index 1ba7aea63..bd4772c79 100644 --- a/src/main/java/org/eolang/jeo/representation/xmir/HexString.java +++ b/src/main/java/org/eolang/jeo/representation/xmir/HexString.java @@ -59,10 +59,17 @@ final class HexString { * @return Human-readable string. */ String decode() { - return Arrays.stream(this.hex.split(" ")) - .map(ch -> (char) Integer.parseInt(ch, HexString.RADIX)) - .map(String::valueOf) - .collect(Collectors.joining()); + try { + return Arrays.stream(this.hex.split(" ")) + .map(ch -> (char) Integer.parseInt(ch, HexString.RADIX)) + .map(String::valueOf) + .collect(Collectors.joining()); + } catch (final NumberFormatException exception) { + throw new IllegalArgumentException( + String.format("Invalid hex string: %s", this.hex), + exception + ); + } } /** diff --git a/src/main/java/org/eolang/jeo/representation/xmir/XmlInstruction.java b/src/main/java/org/eolang/jeo/representation/xmir/XmlInstruction.java index 91cfc6d41..69e11892b 100644 --- a/src/main/java/org/eolang/jeo/representation/xmir/XmlInstruction.java +++ b/src/main/java/org/eolang/jeo/representation/xmir/XmlInstruction.java @@ -31,6 +31,7 @@ import java.util.stream.IntStream; import org.eolang.jeo.representation.HexData; import org.eolang.jeo.representation.bytecode.BytecodeMethod; +import org.objectweb.asm.Label; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -147,6 +148,12 @@ Node node() { * Get opcode arguments. * @param node Node. * @return Arguments. + * @todo #234:90min Refactor XmlInstruction#arguments method. + * This method is too complex and has too many responsibilities. + * Moreover, it uses static field LABELS. We have to refactor this method in order + * to avoid all that problems. Don't forget to remove PMD and checkstyle comments about + * suppressed warnings. + * @checkstyle NestedIfDepthCheck (100 lines) */ private static Object[] arguments(final Node node) { final NodeList children = node.getChildNodes(); @@ -157,6 +164,15 @@ private static Object[] arguments(final Node node) { final NamedNodeMap attributes = child.getAttributes(); if (attributes.getNamedItem("base").getNodeValue().equals("int")) { res.add(new HexString(child.getTextContent()).decodeAsInt()); + } else if (attributes.getNamedItem("base").getNodeValue().equals("label")) { + final String uid = child.getTextContent().strip().replace("\n", ""); + if (XmlLabel.LABELS.containsKey(uid)) { + res.add(XmlLabel.LABELS.get(uid)); + } else { + final Label value = new Label(); + XmlLabel.LABELS.put(uid, value); + res.add(value); + } } else { res.add(new HexString(child.getTextContent()).decode()); } diff --git a/src/main/java/org/eolang/jeo/representation/xmir/XmlLabel.java b/src/main/java/org/eolang/jeo/representation/xmir/XmlLabel.java index 1c28b39c4..6bf0afece 100644 --- a/src/main/java/org/eolang/jeo/representation/xmir/XmlLabel.java +++ b/src/main/java/org/eolang/jeo/representation/xmir/XmlLabel.java @@ -36,14 +36,13 @@ public final class XmlLabel implements XmlCommand { /** * All Labels. - * @todo #226:90min Continue implementation of conditional instructions transformation. - * Currently we parse bytecode and correctly save all labels into XMIR. Although the back - * transformation is not implemented yet. We need to implement it and add or change integration - * tests to check that conditional instructions are correctly transformed. - * Moreover, we have to remove static field LABELS and use different approach to find labels. + * @todo #234:90min Hide LABELS Public Static Variable. + * It is not a good idea to expose LABELS and use static variable in general. + * We definitely have to hide this variable or, what is even better, get rid of it and create + * one more class that will encapsulate the LABELS logic. * @checkstyle StaticVariableNameCheck (3 lines) */ - private static final Map LABELS = new ConcurrentHashMap<>(); + public static final Map LABELS = new ConcurrentHashMap<>(); /** * Label node.