public void writeClosure(ClosureExpression expression) { CompileStack compileStack = controller.getCompileStack(); MethodVisitor mv = controller.getMethodVisitor(); ClassNode classNode = controller.getClassNode(); AsmClassGenerator acg = controller.getAcg(); // generate closure as public class to make sure it can be properly invoked by classes of the // Groovy runtime without circumventing JVM access checks (see CachedMethod for example). ClassNode closureClass = getOrAddClosureClass(expression, ACC_PUBLIC); String closureClassinternalName = BytecodeHelper.getClassInternalName(closureClass); List constructors = closureClass.getDeclaredConstructors(); ConstructorNode node = (ConstructorNode) constructors.get(0); Parameter[] localVariableParams = node.getParameters(); mv.visitTypeInsn(NEW, closureClassinternalName); mv.visitInsn(DUP); if (controller.isStaticMethod() || compileStack.isInSpecialConstructorCall()) { (new ClassExpression(classNode)).visit(acg); (new ClassExpression(controller.getOutermostClass())).visit(acg); } else { mv.visitVarInsn(ALOAD, 0); controller.getOperandStack().push(ClassHelper.OBJECT_TYPE); loadThis(); } // now let's load the various parameters we're passing // we start at index 2 because the first variable we pass // is the owner instance and at this point it is already // on the stack for (int i = 2; i < localVariableParams.length; i++) { Parameter param = localVariableParams[i]; String name = param.getName(); loadReference(name, controller); if (param.getNodeMetaData(ClosureWriter.UseExistingReference.class) == null) { param.setNodeMetaData(ClosureWriter.UseExistingReference.class, Boolean.TRUE); } } // we may need to pass in some other constructors // cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V"); mv.visitMethodInsn( INVOKESPECIAL, closureClassinternalName, "<init>", BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, localVariableParams), false); controller.getOperandStack().replace(ClassHelper.CLOSURE_TYPE, localVariableParams.length); }
protected ClassNode createClosureClass(ClosureExpression expression, int mods) { ClassNode classNode = controller.getClassNode(); ClassNode outerClass = controller.getOutermostClass(); MethodNode methodNode = controller.getMethodNode(); String name = classNode.getName() + "$" + controller .getContext() .getNextClosureInnerName( outerClass, classNode, methodNode); // add a more informative name boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass(); Parameter[] parameters = expression.getParameters(); if (parameters == null) { parameters = Parameter.EMPTY_ARRAY; } else if (parameters.length == 0) { // let's create a default 'it' parameter Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL); parameters = new Parameter[] {it}; Variable ref = expression.getVariableScope().getDeclaredVariable("it"); if (ref != null) it.setClosureSharedVariable(ref.isClosureSharedVariable()); } Parameter[] localVariableParams = getClosureSharedVariables(expression); removeInitialValues(localVariableParams); InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.CLOSURE_TYPE.getPlainNodeReference()); answer.setEnclosingMethod(controller.getMethodNode()); answer.setSynthetic(true); answer.setUsingGenerics(outerClass.isUsingGenerics()); answer.setSourcePosition(expression); if (staticMethodOrInStaticClass) { answer.setStaticClass(true); } if (controller.isInScriptBody()) { answer.setScriptBody(true); } MethodNode method = answer.addMethod( "doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode()); method.setSourcePosition(expression); VariableScope varScope = expression.getVariableScope(); if (varScope == null) { throw new RuntimeException( "Must have a VariableScope by now! for expression: " + expression + " class: " + name); } else { method.setVariableScope(varScope.copy()); } if (parameters.length > 1 || (parameters.length == 1 && parameters[0].getType() != null && parameters[0].getType() != ClassHelper.OBJECT_TYPE && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType()))) { // let's add a typesafe call method MethodNode call = answer.addMethod( "call", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, new ReturnStatement( new MethodCallExpression( VariableExpression.THIS_EXPRESSION, "doCall", new ArgumentListExpression(parameters)))); call.setSourcePosition(expression); } // let's make the constructor BlockStatement block = new BlockStatement(); // this block does not get a source position, because we don't // want this synthetic constructor to show up in corbertura reports VariableExpression outer = new VariableExpression("_outerInstance"); outer.setSourcePosition(expression); block.getVariableScope().putReferencedLocalVariable(outer); VariableExpression thisObject = new VariableExpression("_thisObject"); thisObject.setSourcePosition(expression); block.getVariableScope().putReferencedLocalVariable(thisObject); TupleExpression conArgs = new TupleExpression(outer, thisObject); block.addStatement( new ExpressionStatement(new ConstructorCallExpression(ClassNode.SUPER, conArgs))); // let's assign all the parameter fields from the outer context for (Parameter param : localVariableParams) { String paramName = param.getName(); ClassNode type = param.getType(); if (true) { VariableExpression initialValue = new VariableExpression(paramName); initialValue.setAccessedVariable(param); initialValue.setUseReferenceDirectly(true); ClassNode realType = type; type = ClassHelper.makeReference(); param.setType(ClassHelper.makeReference()); FieldNode paramField = answer.addField(paramName, ACC_PRIVATE | ACC_SYNTHETIC, type, initialValue); paramField.setOriginType(ClassHelper.getWrapper(param.getOriginType())); paramField.setHolder(true); String methodName = Verifier.capitalize(paramName); // let's add a getter & setter Expression fieldExp = new FieldExpression(paramField); answer.addMethod( "get" + methodName, ACC_PUBLIC, realType.getPlainNodeReference(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new ReturnStatement(fieldExp)); } } Parameter[] params = new Parameter[2 + localVariableParams.length]; params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance"); params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject"); System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length); ASTNode sn = answer.addConstructor(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, block); sn.setSourcePosition(expression); correctAccessedVariable(answer, expression); return answer; }