[
https://issues.apache.org/jira/browse/GROOVY-10666?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17572534#comment-17572534
]
Eric Milles commented on GROOVY-10666:
--------------------------------------
I have a working solution for "iterator()". But I have many reservations about
when it gets used and when "getAt(int)" would be used instead. Since iterator
is checked for from the operand type, this is not compatible with dynamic
support for iterator. I suppose a ScriptBytecodeAdapter method would be
required for that. If I went that way, I'm wondering how to ignore the case
where the "iterator(Object)" extension method is the source since it makes
everything iterable.
In {{org.codehaus.groovy.classgen.asm.BinaryExpressionHelper#evaluateEqual}}
replace the "multiple declaration" with:
{code:java}
evaluateEqual((TupleExpression) leftExpression, rhsValueLoader, defineVariable);
private void evaluateEqual(final TupleExpression lhs, final Expression rhs,
final boolean defineVariables) {
AsmClassGenerator acg = controller.getAcg();
CompileStack compileStack = controller.getCompileStack();
OperandStack operandStack = controller.getOperandStack();
// GROOVY-10666: try "iterator()" before "getAt(int)"
MethodNode iterator = rhs.getType().getMethod("iterator",
Parameter.EMPTY_ARRAY);
if (iterator == null) {
iterator =
GeneralUtils.getInterfacesAndSuperInterfaces(rhs.getType()).stream()
.map(in -> in.getMethod("iterator", Parameter.EMPTY_ARRAY))
.filter(Objects::nonNull).findFirst().orElse(null);
}
if (iterator != null) {
MethodCallExpression getIterator = callX(rhs, "iterator");
getIterator.setMethodTarget(iterator);
getIterator.setImplicitThis(false);
getIterator.visit(acg);
int iteratorId = compileStack.defineTemporaryVariable("$iter",
iterator.getReturnType(), true);
Expression theIterator = new
VariableSlotLoader(iterator.getReturnType(), iteratorId, operandStack);
for (Expression e : lhs) {
MethodCallExpression nextElement = callX(theIterator, "next");
nextElement.setMethodTarget(iterator.getReturnType().getDeclaredMethod("next",
Parameter.EMPTY_ARRAY));
nextElement.setImplicitThis(false);
nextElement.visit(acg);
if (defineVariables) {
Variable v = (Variable) e;
operandStack.doGroovyCast(v);
compileStack.defineVariable(v, true);
operandStack.remove(1);
} else {
e.visit(acg);
}
}
compileStack.removeVar(iteratorId);
} else {
int i = 0;
for (Expression e : lhs) {
callX(rhs, "getAt", args(constX(i++))).visit(acg);
if (defineVariables) {
Variable v = (Variable) e;
operandStack.doGroovyCast(v);
compileStack.defineVariable(v, true);
operandStack.remove(1);
} else {
e.visit(acg);
}
}
}
}
{code}
Anyways, I'm probably going to put this one down for now and just use "def
(one,two,three) = stream[0..2]" if I ever wanted multi-assign.
> Implement multiple-assignment (aka destructuring) via getAt(IntRange) or
> iterator()
> -----------------------------------------------------------------------------------
>
> Key: GROOVY-10666
> URL: https://issues.apache.org/jira/browse/GROOVY-10666
> Project: Groovy
> Issue Type: Improvement
> Components: bytecode, groovy-jdk
> Reporter: Eric Milles
> Assignee: Eric Milles
> Priority: Minor
>
> Currently multiple-assignment is implemented using {{getAt(int)}}. For types
> that support {{getAt(IntRange)}} it may be more efficient to use that
> operation. For cases where {{getAt(int)}} is a terminal operation (aka
> streams) it may be the only way to go. Or maybe using one {{iterator()}}
> would be better overall.
> Consider the following:
> {code:groovy}
> Set<String> set = ['foo','bar','baz']
> def (foo, bar, baz) = set // inefficient; requires 3 iterators and 6 calls to
> next
> Stream<String> stream = ['foo','bar','baz'].stream()
> def (foo, bar, baz) = stream // not possible because `getAt(int)` is terminal
> {code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)