in src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java [1798:1937]
public void visitClosureListExpression(final ClosureListExpression expression) {
MethodVisitor mv = controller.getMethodVisitor();
controller.getCompileStack().pushVariableScope(expression.getVariableScope());
List<Expression> expressions = expression.getExpressions();
final int size = expressions.size();
// init declarations
for (int i = 0; i < size; i += 1) {
Expression expr = expressions.get(i);
if (expr instanceof DeclarationExpression) {
DeclarationExpression de = (DeclarationExpression) expr;
BinaryExpression be = new BinaryExpression(
de.getLeftExpression(),
de.getOperation(),
de.getRightExpression());
expressions.set(i, be);
de.setRightExpression(ConstantExpression.NULL);
visitDeclarationExpression(de);
}
}
List<Object> instructions = new LinkedList<>();
// to keep stack height put a null on stack
instructions.add(ConstantExpression.NULL);
// init table
final Label dflt = new Label();
final Label tableEnd = new Label();
final Label[] labels = new Label[size];
instructions.add(new BytecodeInstruction() {
@Override
public void visit(MethodVisitor mv) {
mv.visitVarInsn(ILOAD, 1);
mv.visitTableSwitchInsn(0, size - 1, dflt, labels);
}
});
// visit cases
for (int i = 0; i < size; i += 1) {
Label label = new Label();
Expression expr = expressions.get(i);
labels[i] = label;
instructions.add(new BytecodeInstruction() {
@Override
public void visit(MethodVisitor mv) {
mv.visitLabel(label);
// expressions will leave a value on stack, so need to pop the alibi null
mv.visitInsn(POP);
}
});
instructions.add(expr);
instructions.add(new BytecodeInstruction() {
@Override
public void visit(MethodVisitor mv) {
mv.visitJumpInsn(GOTO, tableEnd);
}
});
}
// default case
instructions.add(new BytecodeInstruction() {
@Override
public void visit(MethodVisitor mv) {
mv.visitLabel(dflt);
}
});
ConstantExpression text = new ConstantExpression("invalid index for closure");
ConstructorCallExpression cce = new ConstructorCallExpression(ClassHelper.make(IllegalArgumentException.class), text);
ThrowStatement ts = new ThrowStatement(cce);
instructions.add(ts);
// return
instructions.add(new BytecodeInstruction() {
@Override
public void visit(MethodVisitor mv) {
mv.visitLabel(tableEnd);
mv.visitInsn(ARETURN);
}
});
BlockStatement bs = new BlockStatement();
bs.addStatement(new BytecodeSequence(instructions));
Parameter closureIndex = new Parameter(ClassHelper.int_TYPE, "__closureIndex");
ClosureExpression ce = new ClosureExpression(new Parameter[]{closureIndex}, bs);
ce.setVariableScope(expression.getVariableScope());
visitClosureExpression(ce);
// we need later an array to store the curried
// closures, so we create it here and ave it
// in a temporary variable
BytecodeHelper.pushConstant(mv, size);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
int listArrayVar = controller.getCompileStack().defineTemporaryVariable("_listOfClosures", true);
// add curried versions
for (int i = 0; i < size; i += 1) {
// stack: closure
// we need to create a curried closure version
// so we store the type on stack
mv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/CurriedClosure");
// stack: closure, type
// for a constructor call we need the type two times
// and the closure after them
mv.visitInsn(DUP2);
mv.visitInsn(SWAP);
// stack: closure,type,type,closure
// so we can create the curried closure
mv.visitInsn(ICONST_1);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitLdcInsn(i);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
mv.visitInsn(AASTORE);
mv.visitMethodInsn(INVOKESPECIAL, "org/codehaus/groovy/runtime/CurriedClosure", "<init>", "(Lgroovy/lang/Closure;[Ljava/lang/Object;)V", false);
// stack: closure,curriedClosure
// we need to save the result
mv.visitVarInsn(ALOAD, listArrayVar);
mv.visitInsn(SWAP);
BytecodeHelper.pushConstant(mv, i);
mv.visitInsn(SWAP);
mv.visitInsn(AASTORE);
// stack: closure
}
// we don't need the closure any longer, so remove it
mv.visitInsn(POP);
// we load the array and create a list from it
mv.visitVarInsn(ALOAD, listArrayVar);
createListMethod.call(mv);
// remove the temporary variable to keep the
// stack clean
controller.getCompileStack().removeVar(listArrayVar);
controller.getOperandStack().pop();
}