diff --git a/pom.xml b/pom.xml index 0e0185fd..26d10dda 100644 --- a/pom.xml +++ b/pom.xml @@ -337,7 +337,7 @@ SOFTWARE. CLASS MISSEDCOUNT - 7 + 8 diff --git a/src/main/java/org/eolang/opeo/SelectiveDecompiler.java b/src/main/java/org/eolang/opeo/SelectiveDecompiler.java index 8f3d1400..7f9dbebc 100644 --- a/src/main/java/org/eolang/opeo/SelectiveDecompiler.java +++ b/src/main/java/org/eolang/opeo/SelectiveDecompiler.java @@ -31,7 +31,7 @@ import java.util.stream.Collectors; import org.eolang.opeo.decompilation.Decompiler; import org.eolang.opeo.decompilation.WithoutAliases; -import org.eolang.opeo.decompilation.handlers.RouterHandler; +import org.eolang.opeo.decompilation.agents.AllAgents; import org.eolang.opeo.jeo.JeoDecompiler; import org.eolang.opeo.storage.FileStorage; import org.eolang.opeo.storage.Storage; @@ -41,7 +41,7 @@ * 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}. + * supported by {@link AllAgents}. * * @since 0.1 */ @@ -69,7 +69,7 @@ public final class SelectiveDecompiler implements Decompiler { * @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()); + this(input, output, modified, new AllAgents(false).supportedOpcodes()); } /** @@ -97,7 +97,7 @@ public SelectiveDecompiler( public SelectiveDecompiler( final Storage storage, final Storage modified ) { - this(storage, modified, new RouterHandler(false).supportedOpcodes()); + this(storage, modified, new AllAgents(false).supportedOpcodes()); } /** diff --git a/src/main/java/org/eolang/opeo/ast/Add.java b/src/main/java/org/eolang/opeo/ast/Addition.java similarity index 94% rename from src/main/java/org/eolang/opeo/ast/Add.java rename to src/main/java/org/eolang/opeo/ast/Addition.java index 906c97c1..8b1783d6 100644 --- a/src/main/java/org/eolang/opeo/ast/Add.java +++ b/src/main/java/org/eolang/opeo/ast/Addition.java @@ -41,7 +41,7 @@ */ @EqualsAndHashCode @ToString -public final class Add implements AstNode, Typed { +public final class Addition implements AstNode, Typed { /** * Left operand. @@ -58,7 +58,7 @@ public final class Add implements AstNode, Typed { * @param node XML node * @param parser Parser */ - public Add(final XmlNode node, final Function parser) { + public Addition(final XmlNode node, final Function parser) { this( parser.apply(node.children().collect(Collectors.toList()).get(0)), parser.apply(node.children().collect(Collectors.toList()).get(1)) @@ -70,7 +70,7 @@ public Add(final XmlNode node, final Function parser) { * @param left Left operand * @param right Right operand */ - public Add(final AstNode left, final AstNode right) { + public Addition(final AstNode left, final AstNode right) { this.left = left; this.right = right; } diff --git a/src/main/java/org/eolang/opeo/ast/Labeled.java b/src/main/java/org/eolang/opeo/ast/Labeled.java index 7e039e66..cb7bc593 100644 --- a/src/main/java/org/eolang/opeo/ast/Labeled.java +++ b/src/main/java/org/eolang/opeo/ast/Labeled.java @@ -30,6 +30,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.eolang.jeo.representation.xmir.XmlNode; +import org.eolang.opeo.decompilation.agents.InvokespecialAgent; import org.objectweb.asm.Type; import org.xembly.Directive; import org.xembly.Directives; @@ -39,7 +40,7 @@ * This class needed to avoid considering labels as separate nodes. * Maybe it's wrong to do so, but it's easier to implement this way, at least for now. * Pay attention, that {@link Labeled} class violates class hierarchy. - * It is the most visible within {@link org.eolang.opeo.decompilation.handlers.InvokespecialHandler} + * It is the most visible within {@link InvokespecialAgent} * implementation. * @since 0.2 */ diff --git a/src/main/java/org/eolang/opeo/ast/Opcode.java b/src/main/java/org/eolang/opeo/ast/Opcode.java index 334fec66..6609eb6c 100644 --- a/src/main/java/org/eolang/opeo/ast/Opcode.java +++ b/src/main/java/org/eolang/opeo/ast/Opcode.java @@ -35,6 +35,7 @@ import org.eolang.jeo.representation.xmir.XmlInstruction; import org.eolang.jeo.representation.xmir.XmlNode; import org.eolang.jeo.representation.xmir.XmlOperand; +import org.eolang.opeo.Instruction; import org.xembly.Directive; /** @@ -119,6 +120,10 @@ public Opcode(final XmlInstruction instruction) { ); } + public Opcode(final Instruction instruction) { + this(instruction.opcode(), instruction.operands()); + } + /** * Constructor. * @param bytecode Bytecode @@ -141,6 +146,40 @@ public List opcodes() { return Arrays.asList(this); } + /** + * Opcode number. + * @return Opcode number. + */ + public int opcode() { + return this.bytecode; + } + + /** + * Opcode operands. + * @return Opcode operands. + */ + public List params() { + return this.operands; + } + + /** + * Instruction operand. + * @param index Operand index. + * @return Instruction operand. + */ + public Object operand(final int index) { + if (this.operands.size() <= index) { + throw new IllegalStateException( + String.format( + "Instruction '%s' doesn't have operand at index '%d'", + this, + index + ) + ); + } + return this.operands.get(index); + } + /** * Disable opcodes counting. * It is useful for tests. diff --git a/src/main/java/org/eolang/opeo/compilation/SelectiveCompiler.java b/src/main/java/org/eolang/opeo/compilation/SelectiveCompiler.java index e163470d..dfed49b0 100644 --- a/src/main/java/org/eolang/opeo/compilation/SelectiveCompiler.java +++ b/src/main/java/org/eolang/opeo/compilation/SelectiveCompiler.java @@ -27,7 +27,7 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.stream.Collectors; -import org.eolang.opeo.decompilation.handlers.RouterHandler; +import org.eolang.opeo.decompilation.agents.AllAgents; import org.eolang.opeo.storage.CompilationStorage; import org.eolang.opeo.storage.Storage; import org.eolang.opeo.storage.XmirEntry; @@ -65,7 +65,7 @@ public SelectiveCompiler(final Path xmirs, final Path output) { */ public SelectiveCompiler(final Storage storage) { this.storage = storage; - this.supported = new RouterHandler(false).supportedOpcodes(); + this.supported = new AllAgents(false).supportedOpcodes(); } @Override diff --git a/src/main/java/org/eolang/opeo/compilation/XmirParser.java b/src/main/java/org/eolang/opeo/compilation/XmirParser.java index b713a855..64044f02 100644 --- a/src/main/java/org/eolang/opeo/compilation/XmirParser.java +++ b/src/main/java/org/eolang/opeo/compilation/XmirParser.java @@ -27,7 +27,7 @@ import java.util.List; import java.util.stream.Collectors; import org.eolang.jeo.representation.xmir.XmlNode; -import org.eolang.opeo.ast.Add; +import org.eolang.opeo.ast.Addition; import org.eolang.opeo.ast.ArrayConstructor; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Attributes; @@ -136,7 +136,7 @@ public AstNode parse(final XmlNode node) { } else if ("duplicated".equals(base)) { result = new Duplicate(this.parse(node.firstChild())); } else if (".plus".equals(base)) { - result = new Add(node, this::parse); + result = new Addition(node, this::parse); } else if (".minus".equals(base)) { result = new Substraction(node, this::parse); } else if ("cast".equals(base)) { diff --git a/src/main/java/org/eolang/opeo/decompilation/InstructionHandler.java b/src/main/java/org/eolang/opeo/decompilation/DecompilationAgent.java similarity index 83% rename from src/main/java/org/eolang/opeo/decompilation/InstructionHandler.java rename to src/main/java/org/eolang/opeo/decompilation/DecompilationAgent.java index 43cd23d5..ed253708 100644 --- a/src/main/java/org/eolang/opeo/decompilation/InstructionHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/DecompilationAgent.java @@ -24,15 +24,15 @@ package org.eolang.opeo.decompilation; /** - * Instruction handler. + * An agent that tries to understand the current decompilation state and apply some changes to it. * @since 0.1 */ @FunctionalInterface -public interface InstructionHandler { +public interface DecompilationAgent { /** - * Handle instruction. - * @param state Current instruction to handle together with operand stack and variables. + * Handle the current state. + * @param state Current state to handle together with operand stack and variables. */ void handle(DecompilerState state); } diff --git a/src/main/java/org/eolang/opeo/decompilation/DecompilerMachine.java b/src/main/java/org/eolang/opeo/decompilation/DecompilerMachine.java index 79c91dbb..1a9223b6 100644 --- a/src/main/java/org/eolang/opeo/decompilation/DecompilerMachine.java +++ b/src/main/java/org/eolang/opeo/decompilation/DecompilerMachine.java @@ -25,11 +25,14 @@ import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; +import java.util.stream.Collectors; import org.cactoos.list.ListOf; import org.eolang.opeo.Instruction; +import org.eolang.opeo.ast.Opcode; import org.eolang.opeo.ast.Root; -import org.eolang.opeo.decompilation.handlers.RouterHandler; +import org.eolang.opeo.decompilation.agents.AllAgents; import org.xembly.Directive; /** @@ -47,7 +50,7 @@ public final class DecompilerMachine { /** * Handler that redirects instructions. */ - private final RouterHandler router; + private final AllAgents agents; /** * Constructor. @@ -73,7 +76,7 @@ public final class DecompilerMachine { */ public DecompilerMachine(final LocalVariables locals, final Map arguments) { this.locals = locals; - this.router = new RouterHandler( + this.agents = new AllAgents( "true".equals(arguments.getOrDefault("counting", "true")) ); } @@ -85,10 +88,15 @@ public DecompilerMachine(final LocalVariables locals, final Map * @return Decompiled instructions. */ public Iterable decompile(final Instruction... instructions) { - final DecompilerState state = new DecompilerState(this.locals); - Arrays.stream(instructions) - .forEach(inst -> this.router.handle(state.next(inst))); - return new Root(new ListOf<>(state.stack().descendingIterator())).toXmir(); + final DecompilerState initial = new DecompilerState( + Arrays.stream(instructions) + .map(Opcode::new) + .collect(Collectors.toCollection(LinkedList::new)), + new OperandStack(), + this.locals + ); + this.agents.handle(initial); + return new Root(new ListOf<>(initial.stack().descendingIterator())).toXmir(); } } diff --git a/src/main/java/org/eolang/opeo/decompilation/DecompilerState.java b/src/main/java/org/eolang/opeo/decompilation/DecompilerState.java index ab204b27..09c7b512 100644 --- a/src/main/java/org/eolang/opeo/decompilation/DecompilerState.java +++ b/src/main/java/org/eolang/opeo/decompilation/DecompilerState.java @@ -23,9 +23,13 @@ */ package org.eolang.opeo.decompilation; +import java.util.Deque; +import java.util.LinkedList; +import java.util.Optional; +import lombok.EqualsAndHashCode; import lombok.ToString; -import org.eolang.opeo.Instruction; import org.eolang.opeo.ast.AstNode; +import org.eolang.opeo.ast.Opcode; import org.objectweb.asm.Type; /** @@ -34,18 +38,21 @@ * @since 0.2 */ @ToString +@EqualsAndHashCode public final class DecompilerState { /** - * Current instruction. + * Remaining opcodes. + * Each method has an original list of opcodes which we decompile. + * When some agent decompiles an instruction, it removes it from this list. */ - private final Instruction current; + private final Deque opcodes; /** * Current operand stack. * It might have some values inside. */ - private final OperandStack operands; + private final OperandStack ostack; /** * Method local variables. @@ -57,31 +64,58 @@ public final class DecompilerState { * @param vars Method local variables. */ public DecompilerState(final LocalVariables vars) { - this(new Instruction.Nop(), new OperandStack(), vars); + this(new OperandStack(), vars); } /** * Constructor. - * @param current Current instruction. + * @param operands Operand stack. + * @param vars Method local variables. + */ + public DecompilerState(final OperandStack operands, final LocalVariables vars) { + this(new LinkedList<>(), operands, vars); + } + + /** + * Constructor. + * @param opcodes Remaining opcodes. * @param stack Operand stack. - * @param variables Method local variables. + * @param vars Method local variables. */ - private DecompilerState( - final Instruction current, + public DecompilerState( + final Deque opcodes, final OperandStack stack, - final LocalVariables variables + final LocalVariables vars ) { - this.current = current; - this.operands = stack; - this.vars = variables; + this.opcodes = opcodes; + this.ostack = stack; + this.vars = vars; } /** * Retrieve current bytecode instruction. * @return Current bytecode instruction. */ - public Instruction instruction() { - return this.current; + public Opcode instruction() { + return Optional.ofNullable(this.opcodes.peek()).orElse(new Opcode(-1)); + } + + /** + * Check if there are any instructions left. + * @return True if there are instructions left. + */ + public boolean hasInstructions() { + return !this.opcodes.isEmpty(); + } + + /** + * Remove current instruction from the list. + * This is used when we decompile an instruction. + */ + public void popInstruction() { + if (!this.opcodes.isEmpty()) { + this.opcodes.pop(); + } } /** @@ -90,16 +124,7 @@ public Instruction instruction() { * @return Instruction operand. */ public Object operand(final int index) { - if (this.current.operands().size() <= index) { - throw new IllegalStateException( - String.format( - "Instruction '%s' doesn't have operand at index '%d'", - this.current, - index - ) - ); - } - return this.current.operand(index); + return this.instruction().operand(index); } /** @@ -117,15 +142,6 @@ public AstNode variable(final int index, final Type type) { * @return Operand stack. */ public OperandStack stack() { - return this.operands; - } - - /** - * Move the state to the next instruction. - * @param instruction Next instruction. - * @return New decompiler state with the next instruction. - */ - DecompilerState next(final Instruction instruction) { - return new DecompilerState(instruction, this.operands, this.vars); + return this.ostack; } } diff --git a/src/main/java/org/eolang/opeo/decompilation/LocalVariables.java b/src/main/java/org/eolang/opeo/decompilation/LocalVariables.java index 47f38c13..bc37efc8 100644 --- a/src/main/java/org/eolang/opeo/decompilation/LocalVariables.java +++ b/src/main/java/org/eolang/opeo/decompilation/LocalVariables.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import lombok.EqualsAndHashCode; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.LocalVariable; import org.eolang.opeo.ast.This; @@ -37,6 +38,7 @@ * Local variables. * @since 0.1 */ +@EqualsAndHashCode public final class LocalVariables { /** diff --git a/src/main/java/org/eolang/opeo/decompilation/OperandStack.java b/src/main/java/org/eolang/opeo/decompilation/OperandStack.java index f5d90d72..37ebdfcd 100644 --- a/src/main/java/org/eolang/opeo/decompilation/OperandStack.java +++ b/src/main/java/org/eolang/opeo/decompilation/OperandStack.java @@ -29,6 +29,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Optional; +import lombok.EqualsAndHashCode; import lombok.ToString; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Label; @@ -44,6 +45,7 @@ * @since 0.2 */ @ToString +@EqualsAndHashCode public final class OperandStack { /** @@ -62,7 +64,7 @@ public final class OperandStack { * Constructor. * @param original Initial stack collection. */ - private OperandStack(final Deque original) { + OperandStack(final Deque original) { this.stack = original; } @@ -108,6 +110,14 @@ public List pop(final int number) { return args; } + /** + * Peek the higher value on the stack. + * @return Node. + */ + public AstNode peek() { + return this.stack.peek(); + } + /** * Push one more node to the stack. * @param node Node to add to the stack. @@ -130,4 +140,5 @@ public void dup() { Iterator descendingIterator() { return this.stack.descendingIterator(); } + } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/AddHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/AddAgent.java similarity index 60% rename from src/main/java/org/eolang/opeo/decompilation/handlers/AddHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/AddAgent.java index 7dd81fdc..e1798516 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/AddHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/AddAgent.java @@ -21,24 +21,44 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; -import org.eolang.opeo.ast.Add; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.eolang.opeo.ast.Addition; import org.eolang.opeo.ast.AstNode; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; /** * Add instruction handler. * @since 0.1 */ -public final class AddHandler implements InstructionHandler { +public final class AddAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.IADD, + Opcodes.LADD, + Opcodes.FADD, + Opcodes.DADD + ) + ); @Override public void handle(final DecompilerState state) { - final AstNode right = state.stack().pop(); - final AstNode left = state.stack().pop(); - state.stack().push(new Add(left, right)); + final int opcode = state.instruction().opcode(); + if (AddAgent.SUPPORTED.contains(opcode)) { + final AstNode right = state.stack().pop(); + final AstNode left = state.stack().pop(); + state.stack().push(new Addition(left, right)); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/agents/AllAgents.java b/src/main/java/org/eolang/opeo/decompilation/agents/AllAgents.java new file mode 100644 index 00000000..792200ee --- /dev/null +++ b/src/main/java/org/eolang/opeo/decompilation/agents/AllAgents.java @@ -0,0 +1,205 @@ +/* + * 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.decompilation.agents; + +import java.util.Map; +import org.cactoos.map.MapEntry; +import org.cactoos.map.MapOf; +import org.eolang.opeo.LabelInstruction; +import org.eolang.opeo.ast.Opcode; +import org.eolang.opeo.ast.OpcodeName; +import org.eolang.opeo.decompilation.DecompilationAgent; +import org.eolang.opeo.decompilation.DecompilerState; +import org.objectweb.asm.Opcodes; + +/** + * All agents that try to decompile incoming instructions. + * @since 0.2 + * @todo #376:90min Decompilation Finish Condition. + * Currently we decompile until we out of instructions. + * But this might be incorrect when we start decompile high-level constructs. + * We should check decompilation stack instead. + * If it changes, we should continue. Otherwise, we should stop. + * The current implementation you can find here {@link #handle(DecompilerState)}. + * @todo #376:90min Unsupported Opcodes. + * Currently we have the ugly map {@link #agents} that contains instructions we can handle. + * We should refactor it to a more elegant solution. + * For example, each agent might provide a list of instructions it can decompile. + * By doing this we can infer the entire list of supported and unsupported instructions. + * @checkstyle ClassFanOutComplexityCheck (500 lines) + */ +public final class AllAgents implements DecompilationAgent { + + /** + * Index of unimplemented agent. + */ + private static final int UNIMPLEMENTED = -1; + + /** + * ALl instruction handlers. + */ + private final Map agents; + + /** + * Constructor. + * @param counting Do we put numbers to opcodes? + */ + public AllAgents(final boolean counting) { + this( + new MapOf( + new MapEntry<>(Opcodes.ICONST_M1, new ConstAgent()), + new MapEntry<>(Opcodes.ICONST_0, new ConstAgent()), + new MapEntry<>(Opcodes.ICONST_1, new ConstAgent()), + new MapEntry<>(Opcodes.ICONST_2, new ConstAgent()), + new MapEntry<>(Opcodes.ICONST_3, new ConstAgent()), + new MapEntry<>(Opcodes.ICONST_4, new ConstAgent()), + new MapEntry<>(Opcodes.ICONST_5, new ConstAgent()), + new MapEntry<>(Opcodes.LCONST_0, new ConstAgent()), + new MapEntry<>(Opcodes.LCONST_1, new ConstAgent()), + new MapEntry<>(Opcodes.FCONST_0, new ConstAgent()), + new MapEntry<>(Opcodes.FCONST_1, new ConstAgent()), + new MapEntry<>(Opcodes.FCONST_2, new ConstAgent()), + new MapEntry<>(Opcodes.DCONST_0, new ConstAgent()), + new MapEntry<>(Opcodes.DCONST_1, new ConstAgent()), + new MapEntry<>(Opcodes.IADD, new AddAgent()), + new MapEntry<>(Opcodes.LADD, new AddAgent()), + new MapEntry<>(Opcodes.FADD, new AddAgent()), + new MapEntry<>(Opcodes.DADD, new AddAgent()), + new MapEntry<>(Opcodes.ISUB, new SubstractionAgent()), + new MapEntry<>(Opcodes.LSUB, new SubstractionAgent()), + new MapEntry<>(Opcodes.FSUB, new SubstractionAgent()), + new MapEntry<>(Opcodes.DSUB, new SubstractionAgent()), + new MapEntry<>(Opcodes.IMUL, new MulAgent()), + new MapEntry<>(Opcodes.IF_ICMPGT, new IfAgent()), + new MapEntry<>(Opcodes.I2B, new CastAgent()), + new MapEntry<>(Opcodes.I2C, new CastAgent()), + new MapEntry<>(Opcodes.I2S, new CastAgent()), + new MapEntry<>(Opcodes.I2L, new CastAgent()), + new MapEntry<>(Opcodes.I2F, new CastAgent()), + new MapEntry<>(Opcodes.I2D, new CastAgent()), + new MapEntry<>(Opcodes.L2I, new CastAgent()), + new MapEntry<>(Opcodes.L2F, new CastAgent()), + new MapEntry<>(Opcodes.L2D, new CastAgent()), + new MapEntry<>(Opcodes.F2I, new CastAgent()), + new MapEntry<>(Opcodes.F2L, new CastAgent()), + new MapEntry<>(Opcodes.F2D, new CastAgent()), + new MapEntry<>(Opcodes.D2I, new CastAgent()), + new MapEntry<>(Opcodes.D2L, new CastAgent()), + new MapEntry<>(Opcodes.D2F, new CastAgent()), + new MapEntry<>(Opcodes.ILOAD, new LoadAgent()), + new MapEntry<>(Opcodes.LLOAD, new LoadAgent()), + new MapEntry<>(Opcodes.FLOAD, new LoadAgent()), + new MapEntry<>(Opcodes.DLOAD, new LoadAgent()), + new MapEntry<>(Opcodes.ALOAD, new LoadAgent()), + new MapEntry<>(Opcodes.ISTORE, new StoreAgent()), + new MapEntry<>(Opcodes.LSTORE, new StoreAgent()), + new MapEntry<>(Opcodes.FSTORE, new StoreAgent()), + new MapEntry<>(Opcodes.DSTORE, new StoreAgent()), + new MapEntry<>(Opcodes.ASTORE, new StoreAgent()), + new MapEntry<>(Opcodes.AASTORE, new StoreToArrayAgent()), + new MapEntry<>(Opcodes.ANEWARRAY, new NewArrayAgent()), + new MapEntry<>(Opcodes.CHECKCAST, new CheckCastAgent()), + new MapEntry<>(Opcodes.NEW, new NewAgent()), + new MapEntry<>(Opcodes.DUP, new DupAgent()), + new MapEntry<>(Opcodes.BIPUSH, new BipushAgent()), + new MapEntry<>(Opcodes.INVOKESPECIAL, new InvokespecialAgent()), + new MapEntry<>(Opcodes.INVOKEVIRTUAL, new InvokevirtualAgent()), + new MapEntry<>(Opcodes.INVOKESTATIC, new InvokestaticAgent()), + new MapEntry<>(Opcodes.INVOKEINTERFACE, new InvokeinterfaceAgent()), + new MapEntry<>(Opcodes.INVOKEDYNAMIC, new InvokedynamicAgent()), + new MapEntry<>(Opcodes.GETFIELD, new GetFieldAgent()), + new MapEntry<>(Opcodes.PUTFIELD, new PutFieldAgent()), + new MapEntry<>(Opcodes.GETSTATIC, new GetStaticAgent()), + new MapEntry<>(Opcodes.LDC, new LdcAgent()), + new MapEntry<>(Opcodes.POP, new PopAgent()), + new MapEntry<>(Opcodes.RETURN, new ReturnAgent()), + new MapEntry<>(Opcodes.IRETURN, new ReturnAgent()), + new MapEntry<>(Opcodes.ARETURN, new ReturnAgent()), + new MapEntry<>(LabelInstruction.LABEL_OPCODE, new LabelAgent()), + new MapEntry<>(AllAgents.UNIMPLEMENTED, new UnimplementedAgent(counting)) + ) + ); + } + + /** + * Constructor. + * @param agents All handlers that will try to handle incoming instructions. + */ + private AllAgents(final Map agents) { + this.agents = agents; + } + + @Override + public void handle(final DecompilerState state) { + while (state.hasInstructions()) { + this.agents.values().forEach(agent -> agent.handle(state)); + } + } + + /** + * Get supported opcodes. + * @return Supported opcodes. + */ + public String[] supportedOpcodes() { + return this.agents.keySet() + .stream() + .map(OpcodeName::new) + .map(OpcodeName::simplified) + .toArray(String[]::new); + } + + /** + * Unimplemented instruction handler. + * @since 0.1 + */ + private static final class UnimplementedAgent implements DecompilationAgent { + + /** + * Do we put numbers to opcodes? + */ + private final boolean counting; + + /** + * Constructor. + * @param counting Flag which decides if we need to count opcodes. + */ + private UnimplementedAgent(final boolean counting) { + this.counting = counting; + } + + @Override + public void handle(final DecompilerState state) { + if (!new AllAgents(false).agents.keySet().contains(state.instruction().opcode())) { + state.stack().push( + new Opcode( + state.instruction().opcode(), + state.instruction().params(), + this.counting + ) + ); + state.popInstruction(); + } + } + } +} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/BipushHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/BipushAgent.java similarity index 68% rename from src/main/java/org/eolang/opeo/decompilation/handlers/BipushHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/BipushAgent.java index e7a46414..ab3cc69f 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/BipushHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/BipushAgent.java @@ -21,21 +21,37 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.Literal; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; /** * Bipush instruction handler. * @since 0.1 */ -public final class BipushHandler implements InstructionHandler { +public final class BipushAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.BIPUSH + ) + ); @Override public void handle(final DecompilerState state) { - state.stack().push(new Literal(state.operand(0))); + if (BipushAgent.SUPPORTED.contains(state.instruction().opcode())) { + state.stack().push(new Literal(state.operand(0))); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/agents/CastAgent.java b/src/main/java/org/eolang/opeo/decompilation/agents/CastAgent.java new file mode 100644 index 00000000..72f77967 --- /dev/null +++ b/src/main/java/org/eolang/opeo/decompilation/agents/CastAgent.java @@ -0,0 +1,126 @@ +/* + * 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.decompilation.agents; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.eolang.opeo.ast.Cast; +import org.eolang.opeo.decompilation.DecompilationAgent; +import org.eolang.opeo.decompilation.DecompilerState; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * Cast instruction handler. + * @since 0.2 + */ +public final class CastAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPROTED = new HashSet<>( + Arrays.asList( + Opcodes.I2B, + Opcodes.I2C, + Opcodes.I2S, + Opcodes.I2L, + Opcodes.I2F, + Opcodes.I2D, + Opcodes.L2I, + Opcodes.L2F, + Opcodes.L2D, + Opcodes.F2I, + Opcodes.F2L, + Opcodes.F2D, + Opcodes.D2I, + Opcodes.D2L, + Opcodes.D2F + ) + ); + + @Override + public void handle(final DecompilerState state) { + final int opcode = state.instruction().opcode(); + if (CastAgent.SUPPROTED.contains(opcode)) { + state.stack().push( + new Cast( + CastAgent.target(opcode), + state.stack().pop() + ) + ); + state.popInstruction(); + } + } + + /** + * Target type. + * @param opcode Opcode to handle. + * @return Target type. + * @checkstyle CyclomaticComplexityCheck (100 lines) + */ + private static Type target(final int opcode) { + final Type result; + switch (opcode) { + case Opcodes.I2B: + result = Type.BYTE_TYPE; + break; + case Opcodes.I2C: + result = Type.CHAR_TYPE; + break; + case Opcodes.I2S: + result = Type.SHORT_TYPE; + break; + case Opcodes.I2L: + case Opcodes.F2L: + case Opcodes.D2L: + result = Type.LONG_TYPE; + break; + case Opcodes.I2F: + case Opcodes.L2F: + case Opcodes.D2F: + result = Type.FLOAT_TYPE; + break; + case Opcodes.I2D: + case Opcodes.L2D: + case Opcodes.F2D: + result = Type.DOUBLE_TYPE; + break; + case Opcodes.L2I: + case Opcodes.F2I: + case Opcodes.D2I: + result = Type.INT_TYPE; + break; + default: + throw new IllegalArgumentException( + String.format( + "Unsupported opcode: %d", + opcode + ) + ); + } + return result; + } +} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/CheckCastHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/CheckCastAgent.java similarity index 65% rename from src/main/java/org/eolang/opeo/decompilation/handlers/CheckCastHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/CheckCastAgent.java index 65f439e1..31cceeaf 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/CheckCastHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/CheckCastAgent.java @@ -21,12 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.CheckCast; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** @@ -34,12 +38,24 @@ * * @since 0.5 */ -public final class CheckCastHandler implements InstructionHandler { +public final class CheckCastAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.CHECKCAST + ) + ); @Override public void handle(final DecompilerState state) { - final AstNode value = state.stack().pop(); - final Object type = state.operand(0); - state.stack().push(new CheckCast(Type.getObjectType((String) type), value)); + if (CheckCastAgent.SUPPORTED.contains(state.instruction().opcode())) { + final AstNode value = state.stack().pop(); + final Object type = state.operand(0); + state.stack().push(new CheckCast(Type.getObjectType((String) type), value)); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/ConstHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/ConstAgent.java similarity index 68% rename from src/main/java/org/eolang/opeo/decompilation/handlers/ConstHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/ConstAgent.java index ecc40e89..d514db5d 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/ConstHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/ConstAgent.java @@ -21,52 +21,88 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Literal; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; /** * Iconst instruction handler. * @since 0.1 + * @checkstyle CyclomaticComplexityCheck (500 lines) */ -public final class ConstHandler implements InstructionHandler { +public final class ConstAgent implements DecompilationAgent { /** - * Type of constant. + * Supported opcodes. */ - private final Type type; - - /** - * Constructor. - * @param type Type of constant - */ - ConstHandler(final Type type) { - this.type = type; - } + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.ICONST_M1, + Opcodes.ICONST_0, + Opcodes.ICONST_1, + Opcodes.ICONST_2, + Opcodes.ICONST_3, + Opcodes.ICONST_4, + Opcodes.ICONST_5, + Opcodes.LCONST_0, + Opcodes.LCONST_1, + Opcodes.FCONST_0, + Opcodes.FCONST_1, + Opcodes.FCONST_2, + Opcodes.DCONST_0, + Opcodes.DCONST_1 + ) + ); @Override public void handle(final DecompilerState state) { final int opcode = state.instruction().opcode(); - final AstNode res; - if (this.type.equals(Type.INT_TYPE)) { - res = ConstHandler.intConstant(opcode); - } else if (this.type.equals(Type.LONG_TYPE)) { - res = ConstHandler.longConstant(opcode); - } else if (this.type.equals(Type.FLOAT_TYPE)) { - res = ConstHandler.floatConstant(opcode); - } else if (this.type.equals(Type.DOUBLE_TYPE)) { - res = ConstHandler.doubleConstant(opcode); - } else { - throw new UnsupportedOperationException( - String.format("Type %s is not supported yet", this.type) - ); + if (ConstAgent.SUPPORTED.contains(opcode)) { + final AstNode res; + switch (opcode) { + case Opcodes.ICONST_M1: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + res = ConstAgent.intConstant(opcode); + state.stack().push(res); + break; + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + res = ConstAgent.longConstant(opcode); + state.stack().push(res); + break; + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + res = ConstAgent.floatConstant(opcode); + state.stack().push(res); + break; + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + res = ConstAgent.doubleConstant(opcode); + state.stack().push(res); + break; + default: + throw new UnsupportedOperationException( + String.format( + "Constant handler for opcode %s is not supported yet", + opcode + ) + ); + } + state.popInstruction(); } - state.stack().push(res); } /** diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/DupHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/DupAgent.java similarity index 68% rename from src/main/java/org/eolang/opeo/decompilation/handlers/DupHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/DupAgent.java index b9d681c3..dc903f5b 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/DupHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/DupAgent.java @@ -21,23 +21,39 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.Duplicate; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; import org.eolang.opeo.decompilation.OperandStack; +import org.objectweb.asm.Opcodes; /** * Dup instruction handler. * @since 0.1 */ -public final class DupHandler implements InstructionHandler { +public final class DupAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.DUP + ) + ); @Override public void handle(final DecompilerState state) { - final OperandStack stack = state.stack(); - stack.push(new Duplicate(stack.pop())); + if (DupAgent.SUPPORTED.contains(state.instruction().opcode())) { + final OperandStack stack = state.stack(); + stack.push(new Duplicate(stack.pop())); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/PutFieldHnadler.java b/src/main/java/org/eolang/opeo/decompilation/agents/GetFieldAgent.java similarity index 67% rename from src/main/java/org/eolang/opeo/decompilation/handlers/PutFieldHnadler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/GetFieldAgent.java index b89060b3..18a36e73 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/PutFieldHnadler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/GetFieldAgent.java @@ -21,40 +21,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; -import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Attributes; -import org.eolang.opeo.ast.Field; -import org.eolang.opeo.ast.FieldAssignment; +import org.eolang.opeo.ast.FieldRetrieval; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; /** - * Putfield instruction handler. - * Stack [before]->[after]: "objectref, value →" + * Getfield instruction handler. * @since 0.1 */ -public final class PutFieldHnadler implements InstructionHandler { +public final class GetFieldAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final AstNode value = state.stack().pop(); - final String name = (String) state.operand(1); - final String owner = (String) state.operand(0); - final String descriptor = (String) state.operand(2); - state.stack().push( - new FieldAssignment( - new Field( + if (state.instruction().opcode() == Opcodes.GETFIELD) { + final String owner = (String) state.operand(0); + final String name = (String) state.operand(1); + final String descriptor = (String) state.operand(2); + state.stack().push( + new FieldRetrieval( state.stack().pop(), new Attributes() .name(name) - .owner(owner) .descriptor(descriptor) - ), - value - ) - ); + .owner(owner) + .type("field") + ) + ); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/GetStaticHnadler.java b/src/main/java/org/eolang/opeo/decompilation/agents/GetStaticAgent.java similarity index 70% rename from src/main/java/org/eolang/opeo/decompilation/handlers/GetStaticHnadler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/GetStaticAgent.java index 329a832d..242708b8 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/GetStaticHnadler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/GetStaticAgent.java @@ -21,22 +21,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; import org.eolang.opeo.ast.ClassField; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; /** * Getstatic instruction handler. * @since 0.1 */ -public final class GetStaticHnadler implements InstructionHandler { +public final class GetStaticAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final String klass = (String) state.operand(0); - final String method = (String) state.operand(1); - final String descriptor = (String) state.operand(2); - state.stack().push(new ClassField(klass, method, descriptor)); + if (state.instruction().opcode() == Opcodes.GETSTATIC) { + final String klass = (String) state.operand(0); + final String method = (String) state.operand(1); + final String descriptor = (String) state.operand(2); + state.stack().push(new ClassField(klass, method, descriptor)); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/IfHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/IfAgent.java similarity index 79% rename from src/main/java/org/eolang/opeo/decompilation/handlers/IfHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/IfAgent.java index edf49385..45113d3a 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/IfHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/IfAgent.java @@ -21,12 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.If; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; import org.eolang.opeo.decompilation.OperandStack; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; @@ -38,23 +41,26 @@ * (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2) * @since 0.2 */ -public final class IfHandler implements InstructionHandler { +public final class IfAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.IF_ICMPGT + ) + ); @Override public void handle(final DecompilerState state) { - if (state.instruction().opcode() == Opcodes.IF_ICMPGT) { + if (IfAgent.SUPPORTED.contains(state.instruction().opcode())) { final OperandStack stack = state.stack(); final AstNode second = stack.pop(); final AstNode first = stack.pop(); final Label operand = (Label) state.operand(0); stack.push(new If(first, second, operand)); - } else { - throw new UnsupportedOperationException( - String.format( - "Unsupported opcode: %d", - state.instruction().opcode() - ) - ); + state.popInstruction(); } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokedynamicHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/InvokedynamicAgent.java similarity index 62% rename from src/main/java/org/eolang/opeo/decompilation/handlers/InvokedynamicHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/InvokedynamicAgent.java index 81e2c8c2..6d85fb82 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokedynamicHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/InvokedynamicAgent.java @@ -21,37 +21,41 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; import java.util.Collections; import java.util.List; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.DynamicInvocation; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** * Invokedynamic instruction handler. * @since 0.5 */ -public final class InvokedynamicHandler implements InstructionHandler { +public final class InvokedynamicAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final List operands = state.instruction().operands(); - final String descriptor = (String) operands.get(1); - final List args = state.stack().pop(Type.getArgumentTypes(descriptor).length); - Collections.reverse(args); - final DynamicInvocation node = new DynamicInvocation( - (String) operands.get(0), - new org.eolang.opeo.ast.Handle((Handle) operands.get(2)), - descriptor, - operands.subList(3, operands.size()), - args - ); - state.stack().push(node); + if (state.instruction().opcode() == Opcodes.INVOKEDYNAMIC) { + final List operands = state.instruction().params(); + final String descriptor = (String) operands.get(1); + final List args = state.stack().pop(Type.getArgumentTypes(descriptor).length); + Collections.reverse(args); + final DynamicInvocation node = new DynamicInvocation( + (String) operands.get(0), + new org.eolang.opeo.ast.Handle((Handle) operands.get(2)), + descriptor, + operands.subList(3, operands.size()), + args + ); + state.stack().push(node); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokeinterfaceHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/InvokeinterfaceAgent.java similarity index 62% rename from src/main/java/org/eolang/opeo/decompilation/handlers/InvokeinterfaceHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/InvokeinterfaceAgent.java index bd29b549..43823505 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokeinterfaceHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/InvokeinterfaceAgent.java @@ -21,15 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; import java.util.Collections; import java.util.List; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Attributes; import org.eolang.opeo.ast.InterfaceInvocation; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** @@ -48,28 +49,31 @@ *

* @since 0.2 */ -public final class InvokeinterfaceHandler implements InstructionHandler { +public final class InvokeinterfaceAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final String owner = (String) state.operand(0); - final String method = (String) state.operand(1); - final String descriptor = (String) state.operand(2); - final boolean interfaced = (Boolean) state.operand(3); - final List args = state.stack().pop( - Type.getArgumentCount(descriptor) - ); - Collections.reverse(args); - final AstNode source = state.stack().pop(); - state.stack().push( - new InterfaceInvocation( - source, - new Attributes() - .name(method) - .descriptor(descriptor) - .interfaced(interfaced) - .owner(owner), - args - ) - ); + if (state.instruction().opcode() == Opcodes.INVOKEINTERFACE) { + final String owner = (String) state.operand(0); + final String method = (String) state.operand(1); + final String descriptor = (String) state.operand(2); + final boolean interfaced = (Boolean) state.operand(3); + final List args = state.stack().pop( + Type.getArgumentCount(descriptor) + ); + Collections.reverse(args); + final AstNode source = state.stack().pop(); + state.stack().push( + new InterfaceInvocation( + source, + new Attributes() + .name(method) + .descriptor(descriptor) + .interfaced(interfaced) + .owner(owner), + args + ) + ); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokespecialHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/InvokespecialAgent.java similarity index 72% rename from src/main/java/org/eolang/opeo/decompilation/handlers/InvokespecialHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/InvokespecialAgent.java index d85e423e..71be3aee 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokespecialHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/InvokespecialAgent.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; import java.util.Collections; import java.util.List; @@ -33,8 +33,9 @@ import org.eolang.opeo.ast.NewAddress; import org.eolang.opeo.ast.Super; import org.eolang.opeo.ast.This; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** @@ -42,14 +43,14 @@ * * @since 0.1 * @todo #229:90min Is Labeled Class an Abstraction Failure? - * As you can see in {@link InvokespecialHandler} we use many 'instanceof' checks. + * As you can see in {@link InvokespecialAgent} we use many 'instanceof' checks. * This is a clear sign that the class hierarchy is not well designed. * The original problem lies in the {@link Labeled} class. * We need to find more elegant solutions to handle labels in AST. * @checkstyle NoJavadocForOverriddenMethodsCheck (500 lines) * @checkstyle MultilineJavadocTagsCheck (500 lines) */ -public final class InvokespecialHandler implements InstructionHandler { +public final class InvokespecialAgent implements DecompilationAgent { /** * Handle invokespecial instruction. @@ -75,31 +76,34 @@ public final class InvokespecialHandler implements InstructionHandler { @Override @SuppressWarnings("PMD.UnusedLocalVariable") public void handle(final DecompilerState state) { - final String type = (String) state.operand(0); - final String name = (String) state.operand(1); - final String descriptor = (String) state.operand(2); - final boolean interfaced = (boolean) state.operand(3); - final List args = state.stack().pop( - Type.getArgumentCount(descriptor) - ); - Collections.reverse(args); - final AstNode target = state.stack().pop(); - if (InvokespecialHandler.isThis(target)) { - state.stack().push( - new Super(target, args, descriptor, type, name) - ); - } else if (this.isNewAddress(target)) { - state.stack().push( - new Constructor( - target, - new Attributes().descriptor(descriptor).interfaced(interfaced), - args - ) - ); - } else { - state.stack().push( - new Super(target, args, descriptor, type, name) + if (state.instruction().opcode() == Opcodes.INVOKESPECIAL) { + final String type = (String) state.operand(0); + final String name = (String) state.operand(1); + final String descriptor = (String) state.operand(2); + final boolean interfaced = (boolean) state.operand(3); + final List args = state.stack().pop( + Type.getArgumentCount(descriptor) ); + Collections.reverse(args); + final AstNode target = state.stack().pop(); + if (InvokespecialAgent.isThis(target)) { + state.stack().push( + new Super(target, args, descriptor, type, name) + ); + } else if (this.isNewAddress(target)) { + state.stack().push( + new Constructor( + target, + new Attributes().descriptor(descriptor).interfaced(interfaced), + args + ) + ); + } else { + state.stack().push( + new Super(target, args, descriptor, type, name) + ); + } + state.popInstruction(); } } @@ -135,9 +139,9 @@ private static boolean isThis(final AstNode candidate) { if (candidate instanceof This) { result = true; } else if (candidate instanceof Labeled) { - result = InvokespecialHandler.isThis(((Labeled) candidate).origin()); + result = InvokespecialAgent.isThis(((Labeled) candidate).origin()); } else if (candidate instanceof Duplicate) { - result = InvokespecialHandler.isThis(((Duplicate) candidate).origin()); + result = InvokespecialAgent.isThis(((Duplicate) candidate).origin()); } else { result = false; } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokestaticHander.java b/src/main/java/org/eolang/opeo/decompilation/agents/InvokestaticAgent.java similarity index 59% rename from src/main/java/org/eolang/opeo/decompilation/handlers/InvokestaticHander.java rename to src/main/java/org/eolang/opeo/decompilation/agents/InvokestaticAgent.java index 00a8ced5..63230793 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokestaticHander.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/InvokestaticAgent.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; import java.util.Collections; import java.util.List; @@ -29,34 +29,38 @@ import org.eolang.opeo.ast.Attributes; import org.eolang.opeo.ast.Owner; import org.eolang.opeo.ast.StaticInvocation; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** * Invokestatic instruction handler. * @since 0.1 */ -public final class InvokestaticHander implements InstructionHandler { +public final class InvokestaticAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final String owner = (String) state.operand(0); - final String method = (String) state.operand(1); - final String descriptor = (String) state.operand(2); - final boolean interfaced = (boolean) state.operand(3); - final List args = state.stack().pop(Type.getArgumentCount(descriptor)); - Collections.reverse(args); - state.stack().push( - new StaticInvocation( - new Attributes() - .name(method) - .descriptor(descriptor) - .owner(owner) - .interfaced(interfaced), - new Owner(owner), - args - ) - ); + if (state.instruction().opcode() == Opcodes.INVOKESTATIC) { + final String owner = (String) state.operand(0); + final String method = (String) state.operand(1); + final String descriptor = (String) state.operand(2); + final boolean interfaced = (boolean) state.operand(3); + final List args = state.stack().pop(Type.getArgumentCount(descriptor)); + Collections.reverse(args); + state.stack().push( + new StaticInvocation( + new Attributes() + .name(method) + .descriptor(descriptor) + .owner(owner) + .interfaced(interfaced), + new Owner(owner), + args + ) + ); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokevirtualHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/InvokevirtualAgent.java similarity index 62% rename from src/main/java/org/eolang/opeo/decompilation/handlers/InvokevirtualHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/InvokevirtualAgent.java index 31fa25fa..f1d8758d 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/InvokevirtualHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/InvokevirtualAgent.java @@ -21,15 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; import java.util.Collections; import java.util.List; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Attributes; import org.eolang.opeo.ast.Invocation; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** @@ -47,29 +48,32 @@ *

* @since 0.1 */ -public final class InvokevirtualHandler implements InstructionHandler { +public final class InvokevirtualAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final String owner = (String) state.operand(0); - final String method = (String) state.operand(1); - final String descriptor = (String) state.operand(2); - final boolean interfaced = (Boolean) state.operand(3); - final List args = state.stack().pop( - Type.getArgumentCount(descriptor) - ); - Collections.reverse(args); - final AstNode source = state.stack().pop(); - state.stack().push( - new Invocation( - source, - new Attributes() - .name(method) - .descriptor(descriptor) - .owner(owner) - .interfaced(interfaced), - args - ) - ); + if (state.instruction().opcode() == Opcodes.INVOKEVIRTUAL) { + final String owner = (String) state.operand(0); + final String method = (String) state.operand(1); + final String descriptor = (String) state.operand(2); + final boolean interfaced = (Boolean) state.operand(3); + final List args = state.stack().pop( + Type.getArgumentCount(descriptor) + ); + Collections.reverse(args); + final AstNode source = state.stack().pop(); + state.stack().push( + new Invocation( + source, + new Attributes() + .name(method) + .descriptor(descriptor) + .owner(owner) + .interfaced(interfaced), + args + ) + ); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/LabelHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/LabelAgent.java similarity index 72% rename from src/main/java/org/eolang/opeo/decompilation/handlers/LabelHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/LabelAgent.java index 5be17b5a..f38ce3a8 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/LabelHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/LabelAgent.java @@ -21,25 +21,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import org.eolang.opeo.LabelInstruction; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Label; import org.eolang.opeo.ast.Labeled; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; /** * Label instruction handler. * @since 0.1 */ -public final class LabelHandler implements InstructionHandler { +public final class LabelAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final Labeled node = new Labeled( - state.stack().first().orElse(new AstNode.Empty()), - new Label(String.class.cast(state.operand(0))) - ); - state.stack().push(node); + if (state.instruction().opcode() == LabelInstruction.LABEL_OPCODE) { + final Labeled node = new Labeled( + state.stack().first().orElse(new AstNode.Empty()), + new Label(String.class.cast(state.operand(0))) + ); + state.stack().push(node); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/LdcHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/LdcAgent.java similarity index 77% rename from src/main/java/org/eolang/opeo/decompilation/handlers/LdcHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/LdcAgent.java index 479e2a06..dbc5fb42 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/LdcHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/LdcAgent.java @@ -21,22 +21,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; import org.eolang.opeo.ast.Constant; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; /** * Ldc instruction handler. * @since 0.1 */ -public final class LdcHandler implements InstructionHandler { +public final class LdcAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final Object operand = state.operand(0); - state.stack().push(new Constant(operand)); + if (state.instruction().opcode() == Opcodes.LDC) { + final Object operand = state.operand(0); + state.stack().push(new Constant(operand)); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/agents/LoadAgent.java b/src/main/java/org/eolang/opeo/decompilation/agents/LoadAgent.java new file mode 100644 index 00000000..43a4c376 --- /dev/null +++ b/src/main/java/org/eolang/opeo/decompilation/agents/LoadAgent.java @@ -0,0 +1,99 @@ +/* + * 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.decompilation.agents; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.eolang.opeo.decompilation.DecompilationAgent; +import org.eolang.opeo.decompilation.DecompilerState; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * Instruction handler. + * This handler might understand the following instructions: + * aload: 1: index | → objectref | load a reference onto the stack from a local variable #index + * aload_0: → objectref | load a reference onto the stack from local variable 0 + * ... + * @since 0.1 + */ +public final class LoadAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.ILOAD, + Opcodes.LLOAD, + Opcodes.FLOAD, + Opcodes.DLOAD, + Opcodes.ALOAD + ) + ); + + @Override + public void handle(final DecompilerState state) { + final int opcode = state.instruction().opcode(); + if (LoadAgent.SUPPORTED.contains(opcode)) { + final Integer index = (Integer) state.operand(0); + state.stack().push( + state.variable(index, LoadAgent.type(opcode)) + ); + state.popInstruction(); + } + } + + /** + * Infer type from opcode. + * @param opcode Opcode + * @return Type + */ + private static Type type(final int opcode) { + final Type result; + switch (opcode) { + case Opcodes.ILOAD: + result = Type.INT_TYPE; + break; + case Opcodes.LLOAD: + result = Type.LONG_TYPE; + break; + case Opcodes.FLOAD: + result = Type.FLOAT_TYPE; + break; + case Opcodes.DLOAD: + result = Type.DOUBLE_TYPE; + break; + case Opcodes.ALOAD: + result = Type.getType(Object.class); + break; + default: + throw new IllegalArgumentException( + String.format("Unsupported opcode: %d", opcode) + ); + } + return result; + } +} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/MulHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/MulAgent.java similarity index 68% rename from src/main/java/org/eolang/opeo/decompilation/handlers/MulHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/MulAgent.java index 280bb6f4..c1814c4a 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/MulHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/MulAgent.java @@ -21,48 +21,42 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Multiplication; -import org.eolang.opeo.ast.Opcode; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; import org.objectweb.asm.Opcodes; /** * Mul instruction handler. * @since 0.1 */ -public final class MulHandler implements InstructionHandler { +public final class MulAgent implements DecompilationAgent { /** - * Do we put numbers to opcodes? + * Supported opcodes. */ - private final boolean counting; - - /** - * Constructor. - * @param counting Do we put numbers to opcodes? - */ - MulHandler(final boolean counting) { - this.counting = counting; - } + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.IMUL, + Opcodes.LMUL, + Opcodes.FMUL, + Opcodes.DMUL + ) + ); @Override public void handle(final DecompilerState state) { - if (state.instruction().opcode() == Opcodes.IMUL) { + if (MulAgent.SUPPORTED.contains(state.instruction().opcode())) { final AstNode right = state.stack().pop(); final AstNode left = state.stack().pop(); state.stack().push(new Multiplication(left, right)); - } else { - state.stack().push( - new Opcode( - state.instruction().opcode(), - state.instruction().operands(), - this.counting - ) - ); + state.popInstruction(); } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/NewHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/NewAgent.java similarity index 68% rename from src/main/java/org/eolang/opeo/decompilation/handlers/NewHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/NewAgent.java index 9cf78c66..4c8242ad 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/NewHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/NewAgent.java @@ -21,21 +21,37 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.NewAddress; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; /** * New instruction handler. * @since 0.1 */ -public final class NewHandler implements InstructionHandler { +public final class NewAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.NEW + ) + ); @Override public void handle(final DecompilerState state) { - state.stack().push(new NewAddress(state.operand(0).toString())); + if (NewAgent.SUPPORTED.contains(state.instruction().opcode())) { + state.stack().push(new NewAddress(state.operand(0).toString())); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/NewArrayHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/NewArrayAgent.java similarity index 65% rename from src/main/java/org/eolang/opeo/decompilation/handlers/NewArrayHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/NewArrayAgent.java index e6d5ab31..4825c7eb 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/NewArrayHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/NewArrayAgent.java @@ -21,27 +21,42 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.ArrayConstructor; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Reference; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; import org.eolang.opeo.decompilation.OperandStack; +import org.objectweb.asm.Opcodes; /** * New array instruction handler. * @since 0.1 */ -public final class NewArrayHandler implements InstructionHandler { +public final class NewArrayAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.ANEWARRAY + ) + ); @Override public void handle(final DecompilerState state) { - final String type = (String) state.operand(0); - final OperandStack stack = state.stack(); - final AstNode size = stack.pop(); - stack.push(new Reference(new ArrayConstructor(size, type))); + if (NewArrayAgent.SUPPORTED.contains(state.instruction().opcode())) { + final String type = (String) state.operand(0); + final OperandStack stack = state.stack(); + final AstNode size = stack.pop(); + stack.push(new Reference(new ArrayConstructor(size, type))); + state.popInstruction(); + } } - } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/PopHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/PopAgent.java similarity index 77% rename from src/main/java/org/eolang/opeo/decompilation/handlers/PopHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/PopAgent.java index cb114de5..3ae38915 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/PopHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/PopAgent.java @@ -21,23 +21,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; import org.eolang.opeo.ast.Popped; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; import org.eolang.opeo.decompilation.OperandStack; +import org.objectweb.asm.Opcodes; /** * Pop instruction handler. * @since 0.1 */ -public final class PopHandler implements InstructionHandler { +public final class PopAgent implements DecompilationAgent { @Override public void handle(final DecompilerState state) { - final OperandStack stack = state.stack(); - stack.push(new Popped(stack.pop())); + if (state.instruction().opcode() == Opcodes.POP) { + final OperandStack stack = state.stack(); + stack.push(new Popped(stack.pop())); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/agents/PutFieldAgent.java b/src/main/java/org/eolang/opeo/decompilation/agents/PutFieldAgent.java new file mode 100644 index 00000000..49674109 --- /dev/null +++ b/src/main/java/org/eolang/opeo/decompilation/agents/PutFieldAgent.java @@ -0,0 +1,63 @@ +/* + * 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.decompilation.agents; + +import org.eolang.opeo.ast.AstNode; +import org.eolang.opeo.ast.Attributes; +import org.eolang.opeo.ast.Field; +import org.eolang.opeo.ast.FieldAssignment; +import org.eolang.opeo.decompilation.DecompilationAgent; +import org.eolang.opeo.decompilation.DecompilerState; +import org.objectweb.asm.Opcodes; + +/** + * Putfield instruction handler. + * Stack [before]->[after]: "objectref, value →" + * @since 0.1 + */ +public final class PutFieldAgent implements DecompilationAgent { + + @Override + public void handle(final DecompilerState state) { + if (state.instruction().opcode() == Opcodes.PUTFIELD) { + final AstNode value = state.stack().pop(); + final String name = (String) state.operand(1); + final String owner = (String) state.operand(0); + final String descriptor = (String) state.operand(2); + state.stack().push( + new FieldAssignment( + new Field( + state.stack().pop(), + new Attributes() + .name(name) + .owner(owner) + .descriptor(descriptor) + ), + value + ) + ); + state.popInstruction(); + } + } +} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/ReturnHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/ReturnAgent.java similarity index 51% rename from src/main/java/org/eolang/opeo/decompilation/handlers/ReturnHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/ReturnAgent.java index fa3d7f00..7d6a2a9f 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/ReturnHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/ReturnAgent.java @@ -21,11 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.Return; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; import org.eolang.opeo.decompilation.OperandStack; import org.objectweb.asm.Opcodes; @@ -34,28 +37,45 @@ * @since 0.1 * @checkstyle ClassFanOutComplexityCheck (500 lines) */ -public final class ReturnHandler implements InstructionHandler { +public final class ReturnAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.RETURN, + Opcodes.IRETURN, + Opcodes.LRETURN, + Opcodes.FRETURN, + Opcodes.DRETURN, + Opcodes.ARETURN + ) + ); @Override public void handle(final DecompilerState state) { final int opcode = state.instruction().opcode(); - final OperandStack stack = state.stack(); - if (opcode == Opcodes.RETURN) { - stack.push(new Return()); - } else if (opcode == Opcodes.IRETURN) { - stack.push(new Return(stack.pop())); - } else if (opcode == Opcodes.LRETURN) { - stack.push(new Return(stack.pop())); - } else if (opcode == Opcodes.FRETURN) { - stack.push(new Return(stack.pop())); - } else if (opcode == Opcodes.DRETURN) { - stack.push(new Return(stack.pop())); - } else if (opcode == Opcodes.ARETURN) { - stack.push(new Return(stack.pop())); - } else { - throw new IllegalStateException( - String.format("Unexpected opcode: %d", opcode) - ); + if (ReturnAgent.SUPPORTED.contains(opcode)) { + final OperandStack stack = state.stack(); + if (opcode == Opcodes.RETURN) { + stack.push(new Return()); + } else if (opcode == Opcodes.IRETURN) { + stack.push(new Return(stack.pop())); + } else if (opcode == Opcodes.LRETURN) { + stack.push(new Return(stack.pop())); + } else if (opcode == Opcodes.FRETURN) { + stack.push(new Return(stack.pop())); + } else if (opcode == Opcodes.DRETURN) { + stack.push(new Return(stack.pop())); + } else if (opcode == Opcodes.ARETURN) { + stack.push(new Return(stack.pop())); + } else { + throw new IllegalStateException( + String.format("Unexpected opcode: %d", opcode) + ); + } + state.popInstruction(); } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/agents/StoreAgent.java b/src/main/java/org/eolang/opeo/decompilation/agents/StoreAgent.java new file mode 100644 index 00000000..031ff32e --- /dev/null +++ b/src/main/java/org/eolang/opeo/decompilation/agents/StoreAgent.java @@ -0,0 +1,122 @@ +/* + * 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.decompilation.agents; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.eolang.opeo.ast.AstNode; +import org.eolang.opeo.ast.LocalVariable; +import org.eolang.opeo.ast.Typed; +import org.eolang.opeo.ast.VariableAssignment; +import org.eolang.opeo.decompilation.DecompilationAgent; +import org.eolang.opeo.decompilation.DecompilerState; +import org.eolang.opeo.decompilation.OperandStack; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * Store instruction handler. + * @since 0.1 + */ +public final class StoreAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.ISTORE, + Opcodes.LSTORE, + Opcodes.FSTORE, + Opcodes.DSTORE, + Opcodes.ASTORE + ) + ); + + @Override + public void handle(final DecompilerState state) { + final int opcode = state.instruction().opcode(); + if (StoreAgent.SUPPORTED.contains(opcode)) { + final OperandStack stack = state.stack(); + final AstNode value = stack.pop(); + stack.push( + new VariableAssignment( + (LocalVariable) state.variable( + (Integer) state.operand(0), StoreAgent.infer(value, opcode) + ), + value + ) + ); + state.popInstruction(); + } + } + + /** + * Infer type of the variable. + * @param value Value to infer type from. + * @param opcode Opcode. + * @return Inferred type. + */ + private static Type infer(final AstNode value, final int opcode) { + final Type result; + if (value instanceof Typed) { + result = ((Typed) value).type(); + } else { + result = StoreAgent.type(opcode); + } + return result; + } + + /** + * Infer type from opcode. + * @param opcode Opcode. + * @return Inferred type. + */ + private static Type type(final int opcode) { + final Type result; + switch (opcode) { + case Opcodes.ISTORE: + result = Type.INT_TYPE; + break; + case Opcodes.LSTORE: + result = Type.LONG_TYPE; + break; + case Opcodes.FSTORE: + result = Type.FLOAT_TYPE; + break; + case Opcodes.DSTORE: + result = Type.DOUBLE_TYPE; + break; + case Opcodes.ASTORE: + result = Type.getType(Object.class); + break; + default: + throw new IllegalArgumentException( + String.format("Unsupported opcode: %d", opcode) + ); + } + return result; + } +} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/StoreToArrayHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/StoreToArrayAgent.java similarity index 71% rename from src/main/java/org/eolang/opeo/decompilation/handlers/StoreToArrayHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/StoreToArrayAgent.java index ec13b8d9..b94fa92f 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/StoreToArrayHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/StoreToArrayAgent.java @@ -21,16 +21,19 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Duplicate; import org.eolang.opeo.ast.FieldRetrieval; import org.eolang.opeo.ast.Labeled; import org.eolang.opeo.ast.Reference; import org.eolang.opeo.ast.StoreArray; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; /** * Store to array instruction handler. @@ -38,24 +41,36 @@ * Opcodes: aastore * Stack [before]->[after]: "arrayref, index, value →" * @since 0.1 - * @todo #329:90min Avoid using 'instance of' in {@link StoreToArrayHandler#findRef(AstNode)}. + * @todo #329:90min Avoid using 'instance of' in {@link StoreToArrayAgent#findRef(AstNode)}. * Here we use 'instance of' statement to find a Reference. * The solution related to Reference looks incorrect, in general. * We should invent a proper solution without the use of this statement. */ -public final class StoreToArrayHandler implements InstructionHandler { +public final class StoreToArrayAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + org.objectweb.asm.Opcodes.AASTORE + ) + ); @Override public void handle(final DecompilerState state) { - final AstNode value = state.stack().pop(); - final AstNode index = state.stack().pop(); - final AstNode array = state.stack().pop(); - try { - final Reference ref = this.findRef(array); - ref.link(new StoreArray(ref.object(), index, value)); - state.stack().push(ref); - } catch (final IllegalStateException exception) { - state.stack().push(new StoreArray(array, index, value)); + if (StoreToArrayAgent.SUPPORTED.contains(state.instruction().opcode())) { + final AstNode value = state.stack().pop(); + final AstNode index = state.stack().pop(); + final AstNode array = state.stack().pop(); + try { + final Reference ref = this.findRef(array); + ref.link(new StoreArray(ref.object(), index, value)); + state.stack().push(ref); + } catch (final IllegalStateException exception) { + state.stack().push(new StoreArray(array, index, value)); + } + state.popInstruction(); } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/SubstractionHandler.java b/src/main/java/org/eolang/opeo/decompilation/agents/SubstractionAgent.java similarity index 62% rename from src/main/java/org/eolang/opeo/decompilation/handlers/SubstractionHandler.java rename to src/main/java/org/eolang/opeo/decompilation/agents/SubstractionAgent.java index f5b66c0d..b17c1402 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/SubstractionHandler.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/SubstractionAgent.java @@ -21,23 +21,43 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Substraction; +import org.eolang.opeo.decompilation.DecompilationAgent; import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; +import org.objectweb.asm.Opcodes; /** * Substraction instruction handler. * @since 0.1 */ -public final class SubstractionHandler implements InstructionHandler { +public final class SubstractionAgent implements DecompilationAgent { + + /** + * Supported opcodes. + */ + private static final Set SUPPORTED = new HashSet<>( + Arrays.asList( + Opcodes.ISUB, + Opcodes.LSUB, + Opcodes.FSUB, + Opcodes.DSUB + ) + ); @Override public void handle(final DecompilerState state) { - final AstNode right = state.stack().pop(); - final AstNode left = state.stack().pop(); - state.stack().push(new Substraction(left, right)); + final int opcode = state.instruction().opcode(); + if (SubstractionAgent.SUPPORTED.contains(opcode)) { + final AstNode right = state.stack().pop(); + final AstNode left = state.stack().pop(); + state.stack().push(new Substraction(left, right)); + state.popInstruction(); + } } } diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/package-info.java b/src/main/java/org/eolang/opeo/decompilation/agents/package-info.java similarity index 96% rename from src/main/java/org/eolang/opeo/decompilation/handlers/package-info.java rename to src/main/java/org/eolang/opeo/decompilation/agents/package-info.java index a520f234..0eaf15e0 100644 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/package-info.java +++ b/src/main/java/org/eolang/opeo/decompilation/agents/package-info.java @@ -29,4 +29,4 @@ * It would also be good to generalize some of the handlers. For example, AddHandler might * handle all the types, not only Integer and Long. */ -package org.eolang.opeo.decompilation.handlers; +package org.eolang.opeo.decompilation.agents; diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/CastHandler.java b/src/main/java/org/eolang/opeo/decompilation/handlers/CastHandler.java deleted file mode 100644 index d151c433..00000000 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/CastHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.decompilation.handlers; - -import org.eolang.opeo.ast.Cast; -import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; -import org.objectweb.asm.Type; - -/** - * Cast instruction handler. - * @since 0.2 - */ -public final class CastHandler implements InstructionHandler { - - /** - * Target type. - */ - private final Type target; - - /** - * Constructor. - * @param target Target type - */ - CastHandler(final Type target) { - this.target = target; - } - - @Override - public void handle(final DecompilerState state) { - state.stack().push( - new Cast( - this.target, - state.stack().pop() - ) - ); - } -} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/GetFieldHandler.java b/src/main/java/org/eolang/opeo/decompilation/handlers/GetFieldHandler.java deleted file mode 100644 index 3f2544f2..00000000 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/GetFieldHandler.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.decompilation.handlers; - -import org.eolang.opeo.ast.Attributes; -import org.eolang.opeo.ast.FieldRetrieval; -import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; - -/** - * Getfield instruction handler. - * @since 0.1 - */ -public final class GetFieldHandler implements InstructionHandler { - - @Override - public void handle(final DecompilerState state) { - final String owner = (String) state.operand(0); - final String name = (String) state.operand(1); - final String descriptor = (String) state.operand(2); - state.stack().push( - new FieldRetrieval( - state.stack().pop(), - new Attributes() - .name(name) - .descriptor(descriptor) - .owner(owner) - .type("field") - ) - ); - } - -} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/LoadHandler.java b/src/main/java/org/eolang/opeo/decompilation/handlers/LoadHandler.java deleted file mode 100644 index 1703958c..00000000 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/LoadHandler.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.decompilation.handlers; - -import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; -import org.objectweb.asm.Type; - -/** - * Instruction handler. - * This handler might understand the following instructions: - * aload: 1: index | → objectref | load a reference onto the stack from a local variable #index - * aload_0: → objectref | load a reference onto the stack from local variable 0 - * ... - * @since 0.1 - */ -public final class LoadHandler implements InstructionHandler { - - /** - * Type of the variable. - */ - private final Type type; - - /** - * Constructor. - * @param type Type of the variable. - */ - public LoadHandler(final Type type) { - this.type = type; - } - - @Override - public void handle(final DecompilerState state) { - final Integer index = (Integer) state.operand(0); - state.stack().push( - state.variable(index, this.type) - ); - } -} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/RouterHandler.java b/src/main/java/org/eolang/opeo/decompilation/handlers/RouterHandler.java deleted file mode 100644 index 81e031a1..00000000 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/RouterHandler.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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.decompilation.handlers; - -import java.util.Map; -import org.cactoos.map.MapEntry; -import org.cactoos.map.MapOf; -import org.eolang.opeo.LabelInstruction; -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.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -/** - * General Instruction Handler. - * This handler redirects handling of instructions depending on an incoming instruction. - * @since 0.2 - * @checkstyle ClassFanOutComplexityCheck (500 lines) - */ -public final class RouterHandler implements InstructionHandler { - - /** - * Index of unimplemented handler. - */ - private static final int UNIMPLEMENTED = -1; - - /** - * ALl instruction handlers. - */ - private final Map handlers; - - /** - * Constructor. - * @param counting Do we put numbers to opcodes? - */ - public RouterHandler(final boolean counting) { - this( - new MapOf( - new MapEntry<>(Opcodes.ICONST_M1, new ConstHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.ICONST_0, new ConstHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.ICONST_1, new ConstHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.ICONST_2, new ConstHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.ICONST_3, new ConstHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.ICONST_4, new ConstHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.ICONST_5, new ConstHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.LCONST_0, new ConstHandler(Type.LONG_TYPE)), - new MapEntry<>(Opcodes.LCONST_1, new ConstHandler(Type.LONG_TYPE)), - new MapEntry<>(Opcodes.FCONST_0, new ConstHandler(Type.FLOAT_TYPE)), - new MapEntry<>(Opcodes.FCONST_1, new ConstHandler(Type.FLOAT_TYPE)), - new MapEntry<>(Opcodes.FCONST_2, new ConstHandler(Type.FLOAT_TYPE)), - new MapEntry<>(Opcodes.DCONST_0, new ConstHandler(Type.DOUBLE_TYPE)), - new MapEntry<>(Opcodes.DCONST_1, new ConstHandler(Type.DOUBLE_TYPE)), - new MapEntry<>(Opcodes.IADD, new AddHandler()), - new MapEntry<>(Opcodes.LADD, new AddHandler()), - new MapEntry<>(Opcodes.FADD, new AddHandler()), - new MapEntry<>(Opcodes.DADD, new AddHandler()), - new MapEntry<>(Opcodes.ISUB, new SubstractionHandler()), - new MapEntry<>(Opcodes.LSUB, new SubstractionHandler()), - new MapEntry<>(Opcodes.FSUB, new SubstractionHandler()), - new MapEntry<>(Opcodes.DSUB, new SubstractionHandler()), - new MapEntry<>(Opcodes.IMUL, new MulHandler(counting)), - new MapEntry<>(Opcodes.IF_ICMPGT, new IfHandler()), - new MapEntry<>(Opcodes.I2B, new CastHandler(Type.BYTE_TYPE)), - new MapEntry<>(Opcodes.I2C, new CastHandler(Type.CHAR_TYPE)), - new MapEntry<>(Opcodes.I2S, new CastHandler(Type.SHORT_TYPE)), - new MapEntry<>(Opcodes.I2L, new CastHandler(Type.LONG_TYPE)), - new MapEntry<>(Opcodes.I2F, new CastHandler(Type.FLOAT_TYPE)), - new MapEntry<>(Opcodes.I2D, new CastHandler(Type.DOUBLE_TYPE)), - new MapEntry<>(Opcodes.L2I, new CastHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.L2F, new CastHandler(Type.FLOAT_TYPE)), - new MapEntry<>(Opcodes.L2D, new CastHandler(Type.DOUBLE_TYPE)), - new MapEntry<>(Opcodes.F2I, new CastHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.F2L, new CastHandler(Type.LONG_TYPE)), - new MapEntry<>(Opcodes.F2D, new CastHandler(Type.DOUBLE_TYPE)), - new MapEntry<>(Opcodes.D2I, new CastHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.D2L, new CastHandler(Type.LONG_TYPE)), - new MapEntry<>(Opcodes.D2F, new CastHandler(Type.FLOAT_TYPE)), - new MapEntry<>(Opcodes.ILOAD, new LoadHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.LLOAD, new LoadHandler(Type.LONG_TYPE)), - new MapEntry<>(Opcodes.FLOAD, new LoadHandler(Type.FLOAT_TYPE)), - new MapEntry<>(Opcodes.DLOAD, new LoadHandler(Type.DOUBLE_TYPE)), - new MapEntry<>(Opcodes.ALOAD, new LoadHandler(Type.getType(Object.class))), - new MapEntry<>(Opcodes.ISTORE, new StoreHandler(Type.INT_TYPE)), - new MapEntry<>(Opcodes.LSTORE, new StoreHandler(Type.LONG_TYPE)), - new MapEntry<>(Opcodes.FSTORE, new StoreHandler(Type.FLOAT_TYPE)), - new MapEntry<>(Opcodes.DSTORE, new StoreHandler(Type.DOUBLE_TYPE)), - new MapEntry<>(Opcodes.ASTORE, new StoreHandler(Type.getType(Object.class))), - new MapEntry<>(Opcodes.AASTORE, new StoreToArrayHandler()), - new MapEntry<>(Opcodes.ANEWARRAY, new NewArrayHandler()), - new MapEntry<>(Opcodes.CHECKCAST, new CheckCastHandler()), - new MapEntry<>(Opcodes.NEW, new NewHandler()), - new MapEntry<>(Opcodes.DUP, new DupHandler()), - new MapEntry<>(Opcodes.BIPUSH, new BipushHandler()), - new MapEntry<>(Opcodes.INVOKESPECIAL, new InvokespecialHandler()), - new MapEntry<>(Opcodes.INVOKEVIRTUAL, new InvokevirtualHandler()), - new MapEntry<>(Opcodes.INVOKESTATIC, new InvokestaticHander()), - new MapEntry<>(Opcodes.INVOKEINTERFACE, new InvokeinterfaceHandler()), - new MapEntry<>(Opcodes.INVOKEDYNAMIC, new InvokedynamicHandler()), - new MapEntry<>(Opcodes.GETFIELD, new GetFieldHandler()), - new MapEntry<>(Opcodes.PUTFIELD, new PutFieldHnadler()), - new MapEntry<>(Opcodes.GETSTATIC, new GetStaticHnadler()), - new MapEntry<>(Opcodes.LDC, new LdcHandler()), - new MapEntry<>(Opcodes.POP, new PopHandler()), - new MapEntry<>(Opcodes.RETURN, new ReturnHandler()), - new MapEntry<>(Opcodes.IRETURN, new ReturnHandler()), - new MapEntry<>(Opcodes.ARETURN, new ReturnHandler()), - new MapEntry<>(LabelInstruction.LABEL_OPCODE, new LabelHandler()), - new MapEntry<>(RouterHandler.UNIMPLEMENTED, new UnimplementedHandler(counting)) - ) - ); - } - - /** - * Constructor. - * @param handlers All handlers that will try to handle incoming instructions. - */ - private RouterHandler(final Map handlers) { - this.handlers = handlers; - } - - @Override - 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. - * @return Instruction handler. - */ - private InstructionHandler handler(final int opcode) { - return this.handlers.getOrDefault(opcode, this.handlers.get(RouterHandler.UNIMPLEMENTED)); - } - - /** - * Unimplemented instruction handler. - * @since 0.1 - */ - private static final class UnimplementedHandler implements InstructionHandler { - - /** - * Do we put numbers to opcodes? - */ - private final boolean counting; - - /** - * Constructor. - * @param counting Flag which decides if we need to count opcodes. - */ - private UnimplementedHandler(final boolean counting) { - this.counting = counting; - } - - @Override - public void handle(final DecompilerState state) { - state.stack().push( - new Opcode( - state.instruction().opcode(), - state.instruction().operands(), - this.counting - ) - ); - } - } -} diff --git a/src/main/java/org/eolang/opeo/decompilation/handlers/StoreHandler.java b/src/main/java/org/eolang/opeo/decompilation/handlers/StoreHandler.java deleted file mode 100644 index 59cd86bc..00000000 --- a/src/main/java/org/eolang/opeo/decompilation/handlers/StoreHandler.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.decompilation.handlers; - -import org.eolang.opeo.ast.AstNode; -import org.eolang.opeo.ast.LocalVariable; -import org.eolang.opeo.ast.Typed; -import org.eolang.opeo.ast.VariableAssignment; -import org.eolang.opeo.decompilation.DecompilerState; -import org.eolang.opeo.decompilation.InstructionHandler; -import org.eolang.opeo.decompilation.OperandStack; -import org.objectweb.asm.Type; - -/** - * Store instruction handler. - * @since 0.1 - */ -public final class StoreHandler implements InstructionHandler { - - /** - * Type of the variable. - */ - private final Type type; - - /** - * Constructor. - * @param type Type of the variable. - */ - public StoreHandler(final Type type) { - this.type = type; - } - - @Override - public void handle(final DecompilerState state) { - final OperandStack stack = state.stack(); - final AstNode value = stack.pop(); - stack.push( - new VariableAssignment( - (LocalVariable) state.variable((Integer) state.operand(0), this.infer(value)), - value - ) - ); - } - - /** - * Infer type of the variable. - * @param value Value to infer type from. - * @return Inferred type. - */ - private Type infer(final AstNode value) { - final Type result; - if (value instanceof Typed) { - result = ((Typed) value).type(); - } else { - result = this.type; - } - return result; - } - -} diff --git a/src/test/java/org/eolang/opeo/ast/AddTest.java b/src/test/java/org/eolang/opeo/ast/AdditionTest.java similarity index 86% rename from src/test/java/org/eolang/opeo/ast/AddTest.java rename to src/test/java/org/eolang/opeo/ast/AdditionTest.java index 94aca802..cca090a9 100644 --- a/src/test/java/org/eolang/opeo/ast/AddTest.java +++ b/src/test/java/org/eolang/opeo/ast/AdditionTest.java @@ -34,14 +34,14 @@ import org.xembly.Xembler; /** - * Test case for {@link Add}. + * Test case for {@link Addition}. * @since 0.1 */ -final class AddTest { +final class AdditionTest { @Test void convertsToXmir() throws ImpossibleModificationException { - final String res = new Xembler(new Add(new Literal(1), new Literal(2)).toXmir()).xml(); + final String res = new Xembler(new Addition(new Literal(1), new Literal(2)).toXmir()).xml(); MatcherAssert.assertThat( String.format( "Can't convert to correct XMIR, result is : %n%s%n", @@ -60,42 +60,42 @@ void convertsToXmir() throws ImpossibleModificationException { void determinesPrimitiveTypesCorrectly() { MatcherAssert.assertThat( "Can't determine the type of Add with two integer literals", - new Add(new Literal(1), new Literal(2)).type(), + new Addition(new Literal(1), new Literal(2)).type(), Matchers.equalTo(Type.INT_TYPE) ); MatcherAssert.assertThat( "Can't determine the type of Add with two long literals", - new Add(new Literal(1L), new Literal(2L)).type(), + new Addition(new Literal(1L), new Literal(2L)).type(), Matchers.equalTo(Type.LONG_TYPE) ); MatcherAssert.assertThat( "Can't determine the type of Add with two float literals", - new Add(new Literal(1.0f), new Literal(2.0f)).type(), + new Addition(new Literal(1.0f), new Literal(2.0f)).type(), Matchers.equalTo(Type.FLOAT_TYPE) ); MatcherAssert.assertThat( "Can't determine the type of Add with two double literals", - new Add(new Literal(1.0), new Literal(2.0)).type(), + new Addition(new Literal(1.0), new Literal(2.0)).type(), Matchers.equalTo(Type.DOUBLE_TYPE) ); MatcherAssert.assertThat( "Can't determine the type of Add with two integer and long literals", - new Add(new Literal(1), new Literal(2L)).type(), + new Addition(new Literal(1), new Literal(2L)).type(), Matchers.equalTo(Type.LONG_TYPE) ); MatcherAssert.assertThat( "Can't determine the type of Add with two integer and float literals", - new Add(new Literal(1), new Literal(2.0f)).type(), + new Addition(new Literal(1), new Literal(2.0f)).type(), Matchers.equalTo(Type.FLOAT_TYPE) ); MatcherAssert.assertThat( "Can't determine the type of Add with two integer and double literals", - new Add(new Literal(1), new Literal(2.0)).type(), + new Addition(new Literal(1), new Literal(2.0)).type(), Matchers.equalTo(Type.DOUBLE_TYPE) ); MatcherAssert.assertThat( "Can't determine the type of Add with two long and float literals", - new Add(new Literal(1L), new Literal(2.0f)).type(), + new Addition(new Literal(1L), new Literal(2.0f)).type(), Matchers.equalTo(Type.FLOAT_TYPE) ); } @@ -105,7 +105,7 @@ void retrievesOpcodesWithLeftAndRightNodesWithTheSameType() { MatcherAssert.assertThat( "Can't retrieve opcodes from Add with two literals", new OpcodeNodes( - new Add( + new Addition( new Literal(1), new Literal(2) ) @@ -123,7 +123,7 @@ void retrievesOpcodesWithLeftAndRightNodesWithDifferentTypes() { MatcherAssert.assertThat( "Can't retrieve opcodes from Add with two literals of different types", new OpcodeNodes( - new Add( + new Addition( new Literal(1L), new Literal(1) ) @@ -141,7 +141,7 @@ void retrievesOpcodesWithLeftAndRightNodesWithDoubleType() { MatcherAssert.assertThat( "Can't retrieve opcodes from Add with where one of the operands is double", new OpcodeNodes( - new Add( + new Addition( new Literal(1.0), new Literal(1) ) diff --git a/src/test/java/org/eolang/opeo/ast/ArrayConstructorTest.java b/src/test/java/org/eolang/opeo/ast/ArrayConstructorTest.java index c9f39145..eaf6bae8 100644 --- a/src/test/java/org/eolang/opeo/ast/ArrayConstructorTest.java +++ b/src/test/java/org/eolang/opeo/ast/ArrayConstructorTest.java @@ -59,10 +59,16 @@ void createsArrayConstructorFromXmir() { "Can't create array constructor from XMIR", new ArrayConstructor( new XmlNode(ArrayConstructorTest.XMIR), - node -> new Add(new Literal(1), new Literal(2)) + node -> new Addition(new Literal(1), new Literal(2)) ), Matchers.equalTo( - new ArrayConstructor(new Add(new Literal(1), new Literal(2)), "java/lang/Integer") + new ArrayConstructor( + new Addition( + new Literal(1), + new Literal(2) + ), + "java/lang/Integer" + ) ) ); } @@ -90,7 +96,7 @@ void compilesArrayWithComplexLength() { "Can't compile array constructor with complex undefined length", new OpcodeNodes( new ArrayConstructor( - new Add(new Literal(1), new Literal(2)), + new Addition(new Literal(1), new Literal(2)), type ) ).opcodes(), @@ -109,7 +115,7 @@ void convertsToXmir() throws ImpossibleModificationException { final String type = "java/lang/Integer"; final String xmir = new Xembler( new ArrayConstructor( - new Add(new Literal(1), new Literal(2)), + new Addition(new Literal(1), new Literal(2)), type ).toXmir() ).xml(); diff --git a/src/test/java/org/eolang/opeo/compilation/XmirParserTest.java b/src/test/java/org/eolang/opeo/compilation/XmirParserTest.java index db8f67d3..9d46d8e3 100644 --- a/src/test/java/org/eolang/opeo/compilation/XmirParserTest.java +++ b/src/test/java/org/eolang/opeo/compilation/XmirParserTest.java @@ -27,7 +27,7 @@ import java.util.List; import org.eolang.jeo.matchers.SameXml; import org.eolang.jeo.representation.xmir.XmlNode; -import org.eolang.opeo.ast.Add; +import org.eolang.opeo.ast.Addition; import org.eolang.opeo.ast.Attributes; import org.eolang.opeo.ast.Field; import org.eolang.opeo.ast.FieldAssignment; @@ -70,7 +70,7 @@ void convertsOpcodesAsIs() { @Test void convertsAddition() { final List nodes = new XmirParser( - new Add(new Literal(1), new Literal(2)) + new Addition(new Literal(1), new Literal(2)) ).toJeoNodes(); MatcherAssert.assertThat( String.format( @@ -96,12 +96,12 @@ void convertsDeepAddition() { MatcherAssert.assertThat( "We expect to retrieve 7 opcodes, but got something else instead", new XmirParser( - new Add( - new Add( + new Addition( + new Addition( new Literal(1), new Literal(2) ), - new Add( + new Addition( new Literal(3), new Literal(4) ) diff --git a/src/test/java/org/eolang/opeo/decompilation/DecompilerMachineTest.java b/src/test/java/org/eolang/opeo/decompilation/DecompilerMachineTest.java index e24ffeb7..1db3a020 100644 --- a/src/test/java/org/eolang/opeo/decompilation/DecompilerMachineTest.java +++ b/src/test/java/org/eolang/opeo/decompilation/DecompilerMachineTest.java @@ -29,7 +29,7 @@ import org.eolang.jeo.representation.xmir.AllLabels; import org.eolang.opeo.LabelInstruction; import org.eolang.opeo.OpcodeInstruction; -import org.eolang.opeo.ast.Add; +import org.eolang.opeo.ast.Addition; import org.eolang.opeo.ast.ArrayConstructor; import org.eolang.opeo.ast.AstNode; import org.eolang.opeo.ast.Attributes; @@ -243,22 +243,23 @@ void decompilesInvokeVirtual() { @Test void decompilesArrayCreation() throws ImpossibleModificationException { final String type = "java/lang/Object"; + final String xml = new Xembler( + new DecompilerMachine() + .decompile( + new OpcodeInstruction(Opcodes.ICONST_2), + new OpcodeInstruction(Opcodes.ICONST_3), + new OpcodeInstruction(Opcodes.IADD), + new OpcodeInstruction(Opcodes.ANEWARRAY, type) + ) + ).xml(); MatcherAssert.assertThat( "Can't decompile array creation", - new Xembler( - new DecompilerMachine() - .decompile( - new OpcodeInstruction(Opcodes.ICONST_2), - new OpcodeInstruction(Opcodes.ICONST_3), - new OpcodeInstruction(Opcodes.IADD), - new OpcodeInstruction(Opcodes.ANEWARRAY, type) - ) - ).xml(), + xml, new SameXml( new Xembler( new Root( new ArrayConstructor( - new Add(new Literal(2), new Literal(3)), + new Addition(new Literal(2), new Literal(3)), type ) ).toXmir() @@ -317,7 +318,7 @@ void decompilesVariableAssignment() { ), new VariableAssignment( new LocalVariable(2, Type.INT_TYPE), - new Add( + new Addition( new LocalVariable(2, Type.INT_TYPE), new Literal(2) ) diff --git a/src/test/java/org/eolang/opeo/decompilation/SelectiveDecompilerTest.java b/src/test/java/org/eolang/opeo/decompilation/SelectiveDecompilerTest.java index d00db47b..bb9753da 100644 --- a/src/test/java/org/eolang/opeo/decompilation/SelectiveDecompilerTest.java +++ b/src/test/java/org/eolang/opeo/decompilation/SelectiveDecompilerTest.java @@ -26,7 +26,7 @@ import java.util.stream.Collectors; import org.cactoos.io.ResourceOf; import org.eolang.opeo.SelectiveDecompiler; -import org.eolang.opeo.decompilation.handlers.RouterHandler; +import org.eolang.opeo.decompilation.agents.AllAgents; import org.eolang.opeo.storage.InMemoryStorage; import org.eolang.opeo.storage.XmirEntry; import org.hamcrest.MatcherAssert; @@ -161,7 +161,7 @@ void avoidsDecompileLargeFileWithUnknownDependencies() { void identifiesUnsupportedOpcodes() { MatcherAssert.assertThat( "We expect that the supported opcodes won't contain the 'GOTO' opcode since we don't support it yet.", - new RouterHandler(false).supportedOpcodes(), + new AllAgents(false).supportedOpcodes(), Matchers.not(Matchers.arrayContaining("GOTO")) ); }