Skip to content

Commit

Permalink
feat(objectionary#329): add arguments of dynamic invocation
Browse files Browse the repository at this point in the history
  • Loading branch information
volodya-lombrozo committed Jul 12, 2024
1 parent c0936e5 commit 7f284d2
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 15 deletions.
104 changes: 93 additions & 11 deletions src/main/java/org/eolang/opeo/ast/DynamicInvocation.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@
package org.eolang.opeo.ast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eolang.jeo.representation.directives.DirectivesData;
import org.eolang.jeo.representation.xmir.HexString;
import org.eolang.jeo.representation.xmir.XmlNode;
import org.eolang.opeo.compilation.Parser;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.xembly.Directive;
Expand All @@ -39,6 +40,7 @@
/**
* Dynamic invocation.
* @since 0.5
* @todo ! test args
*/
public final class DynamicInvocation implements AstNode, Typed {

Expand All @@ -60,14 +62,27 @@ public final class DynamicInvocation implements AstNode, Typed {
/**
* Factory method arguments.
*/
private final List<Object> arguments;
private final List<Object> farguments;

/**
* Dynamic invocation arguments.
*/
private final List<AstNode> arguments;

/**
* Constructor.
* @param root XMIR node to parse.
*/
public DynamicInvocation(final XmlNode root) {
this(root, root.children().collect(Collectors.toList()));
this(root, (node) -> new Empty());
}

/**
* Constructor.
* @param root XMIR node to parse.
*/
public DynamicInvocation(final XmlNode root, final Parser parser) {
this(root, root.children().collect(Collectors.toList()), parser);
}

/**
Expand All @@ -76,12 +91,13 @@ public DynamicInvocation(final XmlNode root) {
* @param root XMIR node to parse.
* @param chldren XMIR node children.
*/
public DynamicInvocation(final XmlNode root, final List<XmlNode> chldren) {
public DynamicInvocation(final XmlNode root, final List<XmlNode> chldren, final Parser parser) {
this(
DynamicInvocation.xname(root),
DynamicInvocation.xfactory(chldren),
DynamicInvocation.xdescriptor(chldren),
DynamicInvocation.xarguments(chldren)
DynamicInvocation.xfarguments(chldren),
DynamicInvocation.xargs(chldren, parser)
);
}

Expand All @@ -99,9 +115,52 @@ public DynamicInvocation(
final String descriptor,
final List<Object> arguments
) {
this.factory = factory;
this.attributes = new Attributes().descriptor(descriptor).type("dynamic");
this(name, factory, descriptor, arguments, Collections.emptyList());
}

/**
* Constructor.
* @param name Name of the method.
* @param factory Factory method reference.
* @param descriptor Method descriptor.
* @param arguments Factory method arguments.
* @checkstyle ParameterNumberCheck (5 lines)
*/
public DynamicInvocation(
final String name,
final Handle factory,
final String descriptor,
final List<Object> farguments,
final List<AstNode> arguments
) {
this(
name,
factory,
new Attributes().descriptor(descriptor).type("dynamic"),
farguments,
arguments
);
}

/**
* Constructor.
* @param name Name of the method.
* @param factory Factory method reference.
* @param attributes Method attributes.
* @param farguments Factory method arguments.
* @param arguments Dynamic invocation arguments.
*/
public DynamicInvocation(
final String name,
final Handle factory,
final Attributes attributes,
final List<Object> farguments,
final List<AstNode> arguments
) {
this.name = name;
this.factory = factory;
this.attributes = attributes;
this.farguments = farguments;
this.arguments = arguments;
}

Expand All @@ -111,14 +170,17 @@ public Iterable<Directive> toXmir() {
.attr("base", String.format(".%s", this.name))
.append(this.attributes.toXmir())
.append(this.factory.toXmir());
DynamicInvocation.xmirArgs(this.arguments).stream().map(Xmir::toXmir)
DynamicInvocation.xmirArgs(this.farguments).stream().map(Xmir::toXmir)
.forEach(directives::append);
this.arguments.stream().map(AstNode::toXmir).forEach(directives::append);
return directives.up();
}

@Override
public List<AstNode> opcodes() {
return Arrays.asList(
final List<AstNode> res = new ArrayList<>(0);
this.arguments.stream().map(AstNode::opcodes).forEach(res::addAll);
res.add(
new Opcode(
Opcodes.INVOKEDYNAMIC,
Stream.concat(
Expand All @@ -127,10 +189,11 @@ public List<AstNode> opcodes() {
this.attributes.descriptor(),
this.factory.toAsm()
),
this.arguments.stream()
this.farguments.stream()
).toArray()
)
);
return res;
}

@Override
Expand Down Expand Up @@ -172,14 +235,33 @@ private static Handle xfactory(final List<XmlNode> children) {
* @param children XMIR children.
* @return Arguments.
*/
private static List<Object> xarguments(final List<XmlNode> children) {
private static List<Object> xfarguments(final List<XmlNode> children) {
final List<Object> res = new ArrayList<>(3);
res.add(Type.getType(new HexString(children.get(2).text()).decode()));
res.add(new Handle(children.get(3)).toAsm());
res.add(Type.getType(new HexString(children.get(4).text()).decode()));
return res;
}

/**
* Parse the dynamic invocation arguments.
* @param children XMIR children.
* @param parser Parser to parse arguments if they exist.
* @return Arguments.
*/
private static List<AstNode> xargs(final List<XmlNode> children, final Parser parser) {
final List<AstNode> result;
if (children.size() > 5) {
result = children.subList(5, children.size())
.stream()
.map(parser::parse)
.collect(Collectors.toList());
} else {
result = Collections.emptyList();
}
return result;
}

/**
* Convert the factory method arguments to XMIR.
* @param args Arguments.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/eolang/opeo/compilation/XmirParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public AstNode parse(final XmlNode node) {
} else if ("interface".equals(attributes.type())) {
result = new InterfaceInvocation(node, this);
} else if ("dynamic".equals(attributes.type())) {
result = new DynamicInvocation(node);
result = new DynamicInvocation(node, this);
} else {
result = new Invocation(node, this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
*/
package org.eolang.opeo.decompilation.handlers;

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.DecompilerState;
import org.eolang.opeo.decompilation.InstructionHandler;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Type;

/**
* Invokedynamic instruction handler.
Expand All @@ -38,11 +41,15 @@ public final class InvokedynamicHandler implements InstructionHandler {
@Override
public void handle(final DecompilerState state) {
final List<Object> operands = state.instruction().operands();
final String descriptor = (String) operands.get(1);
final List<AstNode> 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)),
(String) operands.get(1),
operands.subList(3, operands.size())
descriptor,
operands.subList(3, operands.size()),
args
);
state.stack().push(node);
}
Expand Down
3 changes: 2 additions & 1 deletion src/test/java/it/JeoAndOpeoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ void compilesAlreadyCompiledAndAssembles(final String path) {
"xmir/disassembled/SimpleLog.xmir",
"xmir/disassembled/Factorial.xmir",
"xmir/disassembled/Lambda.xmir",
"xmir/disassembled/Main.xmir"
"xmir/disassembled/Main.xmir",
"xmir/disassembled/UndertowWebServerFactoryCustomizer$ServerOptions.xmir"
})
void decompilesCompilesAndKeepsTheSameInstructions(final String path) throws Exception {
final XMLDocument original = new XMLDocument(new BytesOf(new ResourceOf(path)).asBytes());
Expand Down
Loading

0 comments on commit 7f284d2

Please sign in to comment.