public static Parameter param(ClassNode type, String name, Expression initialExpression) {
   Parameter param = new Parameter(type, name);
   if (initialExpression != null) {
     param.setInitialExpression(initialExpression);
   }
   return param;
 }
Exemplo n.º 2
0
 private static FieldNode createFieldCopy(ClassNode buildee, Parameter param) {
   Map<String, ClassNode> genericsSpec = createGenericsSpec(buildee);
   extractSuperClassGenerics(param.getType(), buildee, genericsSpec);
   ClassNode correctedParamType = correctToGenericsSpecRecurse(genericsSpec, param.getType());
   return new FieldNode(
       param.getName(), ACC_PRIVATE, correctedParamType, buildee, param.getInitialExpression());
 }
  /**
   * Adds the necessary field and methods to support resource locating.
   *
   * @param declaringClass the class to which we add the support field and methods
   */
  public static void apply(@Nonnull ClassNode declaringClass, @Nullable String beanName) {
    injectInterface(declaringClass, EVENT_PUBLISHER_CNODE);

    FieldNode epField =
        injectField(
            declaringClass, EVENT_ROUTER_FIELD_NAME, PRIVATE, EVENT_PUBLISHER_FIELD_CNODE, null);

    Parameter erParam = param(EVENT_ROUTER_CNODE, EVENT_ROUTER_PROPERTY);
    if (!isBlank(beanName)) {
      AnnotationNode namedAnnotation = new AnnotationNode(NAMED_TYPE);
      namedAnnotation.addMember("value", new ConstantExpression(beanName));
      erParam.addAnnotation(namedAnnotation);
    }

    MethodNode setter =
        new MethodNode(
            METHOD_SET_EVENT_ROUTER,
            PRIVATE,
            VOID_TYPE,
            params(erParam),
            NO_EXCEPTIONS,
            stmnt(call(field(epField), METHOD_SET_EVENT_ROUTER, args(var(EVENT_ROUTER_PROPERTY)))));
    setter.addAnnotation(new AnnotationNode(INJECT_TYPE));
    injectMethod(declaringClass, setter);

    addDelegateMethods(declaringClass, EVENT_PUBLISHER_CNODE, field(epField));
  }
  private void doAddConstructor(final ClassNode cNode, final ConstructorNode constructorNode) {
    cNode.addConstructor(constructorNode);
    // GROOVY-5814: Immutable is not compatible with @CompileStatic
    Parameter argsParam = null;
    for (Parameter p : constructorNode.getParameters()) {
      if ("args".equals(p.getName())) {
        argsParam = p;
        break;
      }
    }
    if (argsParam != null) {
      final Parameter arg = argsParam;
      ClassCodeVisitorSupport variableExpressionFix =
          new ClassCodeVisitorSupport() {
            @Override
            protected SourceUnit getSourceUnit() {
              return cNode.getModule().getContext();
            }

            @Override
            public void visitVariableExpression(final VariableExpression expression) {
              super.visitVariableExpression(expression);
              if ("args".equals(expression.getName())) {
                expression.setAccessedVariable(arg);
              }
            }
          };
      variableExpressionFix.visitConstructor(constructorNode);
    }
  }
 public void visitCatchStatement(CatchStatement statement) {
   pushState();
   Parameter p = statement.getVariable();
   p.setInStaticContext(currentScope.isInStaticContext());
   declare(p, statement);
   super.visitCatchStatement(statement);
   popState();
 }
 private void declare(Parameter[] parameters, ASTNode node) {
   for (Parameter parameter : parameters) {
     if (parameter.hasInitialExpression()) {
       parameter.getInitialExpression().visit(this);
     }
     declare(parameter, node);
   }
 }
 public static String makeDescriptorWithoutReturnType(MethodNode mn) {
   StringBuilder sb = new StringBuilder();
   sb.append(mn.getName()).append(':');
   for (Parameter p : mn.getParameters()) {
     sb.append(p.getType()).append(',');
   }
   return sb.toString();
 }
 public static Parameter[] cloneParams(Parameter[] source) {
   Parameter[] result = new Parameter[source.length];
   for (int i = 0; i < source.length; i++) {
     Parameter srcParam = source[i];
     Parameter dstParam = new Parameter(srcParam.getOriginType(), srcParam.getName());
     result[i] = dstParam;
   }
   return result;
 }
 private void memorizeInitialExpressions(final MethodNode node) {
   // add node metadata for default parameters because they are erased by the Verifier
   if (node.getParameters() != null) {
     for (Parameter parameter : node.getParameters()) {
       parameter.putNodeMetaData(
           StaticTypesMarker.INITIAL_EXPRESSION, parameter.getInitialExpression());
     }
   }
 }
Exemplo n.º 10
0
 /*
  * this method is called for local variables shared between scopes.
  * These variables must not have init values because these would
  * then in later steps be used to create multiple versions of the
  * same method, in this case the constructor. A closure should not
  * have more than one constructor!
  */
 private void removeInitialValues(Parameter[] params) {
   for (int i = 0; i < params.length; i++) {
     if (params[i].hasInitialExpression()) {
       Parameter p = new Parameter(params[i].getType(), params[i].getName());
       p.setOriginType(p.getOriginType());
       params[i] = p;
     }
   }
 }
 public void visitForLoop(ForStatement forLoop) {
   pushState();
   forLoop.setVariableScope(currentScope);
   Parameter p = forLoop.getVariable();
   p.setInStaticContext(currentScope.isInStaticContext());
   if (p != ForStatement.FOR_LOOP_DUMMY) declare(p, forLoop);
   super.visitForLoop(forLoop);
   popState();
 }
Exemplo n.º 12
0
 private ClassNode getConstructorArgumentType(Expression arg, ConstructorNode node) {
   if (!(arg instanceof VariableExpression)) return arg.getType();
   VariableExpression vexp = (VariableExpression) arg;
   String name = vexp.getName();
   for (Parameter param : node.getParameters()) {
     if (param.getName().equals(name)) {
       return param.getType();
     }
   }
   return vexp.getType();
 }
Exemplo n.º 13
0
  /**
   * Creates an argument list from the given parameter types.
   *
   * @param parameterTypes The parameter types
   * @param thisAsFirstArgument Whether to include a reference to 'this' as the first argument
   * @return the arguments
   */
  public static ArgumentListExpression createArgumentListFromParameters(
      Parameter[] parameterTypes, boolean thisAsFirstArgument) {
    ArgumentListExpression arguments = new ArgumentListExpression();

    if (thisAsFirstArgument) {
      arguments.addExpression(AbstractGrailsArtefactTransformer.THIS_EXPRESSION);
    }

    for (Parameter parameterType : parameterTypes) {
      arguments.addExpression(new VariableExpression(parameterType.getName()));
    }
    return arguments;
  }
Exemplo n.º 14
0
 protected Parameter[] getClosureSharedVariables(ClosureExpression ce) {
   VariableScope scope = ce.getVariableScope();
   Parameter[] ret = new Parameter[scope.getReferencedLocalVariablesCount()];
   int index = 0;
   for (Iterator iter = scope.getReferencedLocalVariablesIterator(); iter.hasNext(); ) {
     Variable element = (org.codehaus.groovy.ast.Variable) iter.next();
     Parameter p = new Parameter(element.getType(), element.getName());
     p.setOriginType(element.getOriginType());
     p.setClosureSharedVariable(element.isClosureSharedVariable());
     ret[index] = p;
     index++;
   }
   return ret;
 }
Exemplo n.º 15
0
  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);
  }
  private Statement makeMethodBody(
      ClassNode classNode, MethodSignature methodSignature, Parameter[] parameters) {
    String invokeMethod = methodSignature.isStatic() ? "invokeStatic" : "invokeInstance";
    Expression[] args = new Expression[parameters.length + 2];
    args[0] =
        methodSignature.isStatic()
            ? new ClassExpression(classNode)
            : VariableExpression.THIS_EXPRESSION;
    args[1] = new ConstantExpression(methodSignature.getMethodName());
    int i = 2;
    for (Parameter parameter : parameters) {
      args[i++] = new VariableExpression(parameter.getName());
    }

    return returns(call(getDomainHandlerMethod(classNode), invokeMethod, args(args)));
  }
Exemplo n.º 17
0
 private List<FieldNode> convertParamsToFields(ClassNode builder, Parameter[] parameters) {
   List<FieldNode> fieldNodes = new ArrayList<FieldNode>();
   for (Parameter parameter : parameters) {
     Map<String, ClassNode> genericsSpec = createGenericsSpec(builder);
     ClassNode correctedType = correctToGenericsSpecRecurse(genericsSpec, parameter.getType());
     FieldNode fieldNode =
         new FieldNode(
             parameter.getName(),
             parameter.getModifiers(),
             correctedType,
             builder,
             DEFAULT_INITIAL_VALUE);
     fieldNodes.add(fieldNode);
     builder.addField(fieldNode);
   }
   return fieldNodes;
 }
Exemplo n.º 18
0
 private void addInvalidUseOfFinalError(
     MethodNode method, Parameter[] parameters, ClassNode superCN) {
   StringBuilder msg = new StringBuilder();
   msg.append("You are not allowed to override the final method ").append(method.getName());
   msg.append("(");
   boolean needsComma = false;
   for (Parameter parameter : parameters) {
     if (needsComma) {
       msg.append(",");
     } else {
       needsComma = true;
     }
     msg.append(parameter.getType());
   }
   msg.append(") from ").append(getDescription(superCN));
   msg.append(".");
   addError(msg.toString(), method);
 }
Exemplo n.º 19
0
 /**
  * If constructor does not define a call to super, then transform constructor to get String,int
  * parameters at beginning and add call super(String,int).
  */
 private void transformConstructor(ConstructorNode ctor, boolean isAic) {
   boolean chainedThisConstructorCall = false;
   ConstructorCallExpression cce = null;
   if (ctor.firstStatementIsSpecialConstructorCall()) {
     Statement code = ctor.getFirstStatement();
     cce = (ConstructorCallExpression) ((ExpressionStatement) code).getExpression();
     if (cce.isSuperCall()) return;
     // must be call to this(...)
     chainedThisConstructorCall = true;
   }
   // we need to add parameters
   Parameter[] oldP = ctor.getParameters();
   Parameter[] newP = new Parameter[oldP.length + 2];
   String stringParameterName = getUniqueVariableName("__str", ctor.getCode());
   newP[0] = new Parameter(ClassHelper.STRING_TYPE, stringParameterName);
   String intParameterName = getUniqueVariableName("__int", ctor.getCode());
   newP[1] = new Parameter(ClassHelper.int_TYPE, intParameterName);
   System.arraycopy(oldP, 0, newP, 2, oldP.length);
   ctor.setParameters(newP);
   VariableExpression stringVariable = new VariableExpression(newP[0]);
   VariableExpression intVariable = new VariableExpression(newP[1]);
   if (chainedThisConstructorCall) {
     TupleExpression args = (TupleExpression) cce.getArguments();
     List<Expression> argsExprs = args.getExpressions();
     argsExprs.add(0, stringVariable);
     argsExprs.add(1, intVariable);
   } else {
     // add a super call
     List<Expression> args = new ArrayList<Expression>();
     args.add(stringVariable);
     args.add(intVariable);
     if (isAic) {
       for (Parameter parameter : oldP) {
         args.add(new VariableExpression(parameter.getName()));
       }
     }
     cce = new ConstructorCallExpression(ClassNode.SUPER, new ArgumentListExpression(args));
     BlockStatement code = new BlockStatement();
     code.addStatement(new ExpressionStatement(cce));
     Statement oldCode = ctor.getCode();
     if (oldCode != null) code.addStatement(oldCode);
     ctor.setCode(code);
   }
 }
Exemplo n.º 20
0
  /**
   * Adds a static method to the given class node that delegates to the given method and resolves
   * the object to invoke the method on from the given expression.
   *
   * @param expression The expression
   * @param classNode The class node
   * @param delegateMethod The delegate method
   * @return The added method node or null if it couldn't be added
   */
  public static MethodNode addDelegateStaticMethod(
      Expression expression, ClassNode classNode, MethodNode delegateMethod) {
    Parameter[] parameterTypes = delegateMethod.getParameters();
    String declaredMethodName = delegateMethod.getName();
    if (classNode.hasDeclaredMethod(declaredMethodName, parameterTypes)) {
      return null;
    }

    BlockStatement methodBody = new BlockStatement();
    ArgumentListExpression arguments = new ArgumentListExpression();

    for (Parameter parameterType : parameterTypes) {
      arguments.addExpression(new VariableExpression(parameterType.getName()));
    }
    MethodCallExpression methodCallExpression =
        new MethodCallExpression(expression, declaredMethodName, arguments);
    methodCallExpression.setMethodTarget(delegateMethod);

    ThrowStatement missingMethodException = createMissingMethodThrowable(classNode, delegateMethod);
    VariableExpression apiVar = addApiVariableDeclaration(expression, delegateMethod, methodBody);
    IfStatement ifStatement =
        createIfElseStatementForApiMethodCall(methodCallExpression, apiVar, missingMethodException);

    methodBody.addStatement(ifStatement);
    ClassNode returnType = nonGeneric(delegateMethod.getReturnType());
    if (METHOD_MISSING_METHOD_NAME.equals(declaredMethodName)) {
      declaredMethodName = STATIC_METHOD_MISSING_METHOD_NAME;
    }
    MethodNode methodNode = classNode.getDeclaredMethod(declaredMethodName, parameterTypes);
    if (methodNode == null) {
      methodNode =
          new MethodNode(
              declaredMethodName,
              Modifier.PUBLIC | Modifier.STATIC,
              returnType,
              copyParameters(parameterTypes),
              GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY,
              methodBody);
      methodNode.addAnnotations(delegateMethod.getAnnotations());

      classNode.addMethod(methodNode);
    }
    return methodNode;
  }
  @Test
  public void transformationOfAnnotationOnMethodParameter() {
    ClassNode classNode = new ClassNode("Test", 0, new ClassNode(Object.class));
    this.moduleNode.addClass(classNode);

    Parameter parameter = new Parameter(new ClassNode(Object.class), "test");
    parameter.addAnnotation(this.grabAnnotation);

    MethodNode methodNode =
        new MethodNode(
            "test",
            0,
            new ClassNode(Void.class),
            new Parameter[] {parameter},
            new ClassNode[0],
            null);
    classNode.addMethod(methodNode);

    assertGrabAnnotationHasBeenTransformed();
  }
  public void visitClosureExpression(ClosureExpression expression) {
    pushState();

    expression.setVariableScope(currentScope);

    if (expression.isParameterSpecified()) {
      Parameter[] parameters = expression.getParameters();
      for (Parameter parameter : parameters) {
        parameter.setInStaticContext(currentScope.isInStaticContext());
        if (parameter.hasInitialExpression()) {
          parameter.getInitialExpression().visit(this);
        }
        declare(parameter, expression);
      }
    } else if (expression.getParameters() != null) {
      Parameter var = new Parameter(ClassHelper.OBJECT_TYPE, "it");
      var.setInStaticContext(currentScope.isInStaticContext());
      currentScope.putDeclaredVariable(var);
    }

    super.visitClosureExpression(expression);
    markClosureSharedVariables();

    popState();
  }
Exemplo n.º 23
0
 public void buildMethod(
     BuilderASTTransformation transform, MethodNode mNode, AnnotationNode anno) {
   if (transform.getMemberValue(anno, "includes") != null
       || transform.getMemberValue(anno, "includes") != null) {
     transform.addError(
         "Error during "
             + BuilderASTTransformation.MY_TYPE_NAME
             + " processing: includes/excludes only allowed on classes",
         anno);
   }
   ClassNode buildee = mNode.getDeclaringClass();
   ClassNode builder = createBuilder(anno, buildee);
   createBuilderFactoryMethod(anno, buildee, builder);
   for (Parameter parameter : mNode.getParameters()) {
     builder.addField(createFieldCopy(buildee, parameter));
     builder.addMethod(
         createBuilderMethodForProp(
             builder,
             new PropertyInfo(parameter.getName(), parameter.getType()),
             getPrefix(anno)));
   }
   builder.addMethod(createBuildMethodForMethod(anno, buildee, mNode, mNode.getParameters()));
 }
Exemplo n.º 24
0
 private void addWeakerAccessError(
     ClassNode cn, MethodNode method, Parameter[] parameters, MethodNode superMethod) {
   StringBuilder msg = new StringBuilder();
   msg.append(method.getName());
   msg.append("(");
   boolean needsComma = false;
   for (Parameter parameter : parameters) {
     if (needsComma) {
       msg.append(",");
     } else {
       needsComma = true;
     }
     msg.append(parameter.getType());
   }
   msg.append(") in ");
   msg.append(cn.getName());
   msg.append(" cannot override ");
   msg.append(superMethod.getName());
   msg.append(" in ");
   msg.append(superMethod.getDeclaringClass().getName());
   msg.append("; attempting to assign weaker access privileges; was ");
   msg.append(superMethod.isPublic() ? "public" : "protected");
   addError(msg.toString(), method);
 }
Exemplo n.º 25
0
 private static Parameter[] copyParameters(Parameter[] parameterTypes) {
   Parameter[] newParameterTypes = new Parameter[parameterTypes.length];
   for (int i = 0; i < parameterTypes.length; i++) {
     Parameter parameterType = parameterTypes[i];
     Parameter newParameter =
         new Parameter(
             nonGeneric(parameterType.getType()),
             parameterType.getName(),
             parameterType.getInitialExpression());
     newParameter.addAnnotations(parameterType.getAnnotations());
     newParameterTypes[i] = newParameter;
   }
   return newParameterTypes;
 }
  private Set<String> getPropertyNamesToIncludeInWhiteList(
      final SourceUnit sourceUnit, final ClassNode classNode) {
    if (CLASS_NAME_TO_WHITE_LIST_PROPERTY_NAMES.containsKey(classNode)) {
      return CLASS_NAME_TO_WHITE_LIST_PROPERTY_NAMES.get(classNode);
    }
    final Set<String> propertyNamesToIncludeInWhiteList = new HashSet<String>();
    final Set<String> unbindablePropertyNames = new HashSet<String>();
    final Set<String> bindablePropertyNames = new HashSet<String>();
    if (!classNode.getSuperClass().equals(new ClassNode(Object.class))) {
      final Set<String> parentClassPropertyNames =
          getPropertyNamesToIncludeInWhiteList(sourceUnit, classNode.getSuperClass());
      bindablePropertyNames.addAll(parentClassPropertyNames);
    }

    final FieldNode constraintsFieldNode = classNode.getDeclaredField(CONSTRAINTS_FIELD_NAME);
    if (constraintsFieldNode != null && constraintsFieldNode.hasInitialExpression()) {
      final Expression constraintsInitialExpression = constraintsFieldNode.getInitialExpression();
      if (constraintsInitialExpression instanceof ClosureExpression) {

        final Map<String, Map<String, Expression>> constraintsInfo =
            GrailsASTUtils.getConstraintMetadata((ClosureExpression) constraintsInitialExpression);

        for (Entry<String, Map<String, Expression>> constraintConfig : constraintsInfo.entrySet()) {
          final String propertyName = constraintConfig.getKey();
          final Map<String, Expression> mapEntryExpressions = constraintConfig.getValue();
          for (Entry<String, Expression> entry : mapEntryExpressions.entrySet()) {
            final String constraintName = entry.getKey();
            if (BINDABLE_CONSTRAINT_NAME.equals(constraintName)) {
              final Expression valueExpression = entry.getValue();
              Boolean bindableValue = null;
              if (valueExpression instanceof ConstantExpression) {
                final Object constantValue = ((ConstantExpression) valueExpression).getValue();
                if (constantValue instanceof Boolean) {
                  bindableValue = (Boolean) constantValue;
                }
              }
              if (bindableValue != null) {
                if (Boolean.TRUE.equals(bindableValue)) {
                  unbindablePropertyNames.remove(propertyName);
                  bindablePropertyNames.add(propertyName);
                } else {
                  bindablePropertyNames.remove(propertyName);
                  unbindablePropertyNames.add(propertyName);
                }
              } else {
                final String message =
                    "The bindable constraint for property ["
                        + propertyName
                        + "] in class ["
                        + classNode.getName()
                        + "] has a value which is not a boolean literal and will be ignored.";
                GrailsASTUtils.warning(sourceUnit, valueExpression, message);
              }
            }
          }
        }
      }
    }

    final Set<String> fieldsInTransientsList = getPropertyNamesExpressedInTransientsList(classNode);

    propertyNamesToIncludeInWhiteList.addAll(bindablePropertyNames);
    final List<FieldNode> fields = classNode.getFields();
    for (FieldNode fieldNode : fields) {
      final String fieldName = fieldNode.getName();
      if ((!unbindablePropertyNames.contains(fieldName))
          && (bindablePropertyNames.contains(fieldName)
              || shouldFieldBeInWhiteList(fieldNode, fieldsInTransientsList))) {
        propertyNamesToIncludeInWhiteList.add(fieldName);
      }
    }

    final Map<String, MethodNode> declaredMethodsMap = classNode.getDeclaredMethodsMap();
    for (Entry<String, MethodNode> methodEntry : declaredMethodsMap.entrySet()) {
      final MethodNode value = methodEntry.getValue();
      if (value.getDeclaringClass() == classNode) {
        Parameter[] parameters = value.getParameters();
        if (parameters != null && parameters.length == 1) {
          final String methodName = value.getName();
          if (methodName.startsWith("set")) {
            final Parameter parameter = parameters[0];
            final ClassNode paramType = parameter.getType();
            if (!paramType.equals(new ClassNode(Object.class))) {
              final String restOfMethodName = methodName.substring(3);
              final String propertyName = GrailsNameUtils.getPropertyName(restOfMethodName);
              if (!unbindablePropertyNames.contains(propertyName)) {
                propertyNamesToIncludeInWhiteList.add(propertyName);
              }
            }
          }
        }
      }
    }
    CLASS_NAME_TO_WHITE_LIST_PROPERTY_NAMES.put(classNode, propertyNamesToIncludeInWhiteList);
    Map<String, ClassNode> allAssociationMap = GrailsASTUtils.getAllAssociationMap(classNode);
    for (String associationName : allAssociationMap.keySet()) {
      if (!propertyNamesToIncludeInWhiteList.contains(associationName)) {
        propertyNamesToIncludeInWhiteList.add(associationName);
      }
    }
    return propertyNamesToIncludeInWhiteList;
  }
Exemplo n.º 27
0
 private void checkGenericsUsage(ASTNode ref, Parameter[] params) {
   for (Parameter p : params) {
     checkGenericsUsage(ref, p.getType());
   }
 }
Exemplo n.º 28
0
  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;
  }
  public static void apply(ClassNode declaringClass) {
    injectInterface(declaringClass, MYBATIS_CONTRIBUTION_HANDLER_CNODE);

    // add field:
    // protected MybatisProvider this$MybatisProvider = DefaultMybatisProvider.instance
    FieldNode providerField =
        declaringClass.addField(
            MYBATIS_PROVIDER_FIELD_NAME,
            ACC_PRIVATE | ACC_SYNTHETIC,
            MYBATIS_PROVIDER_CNODE,
            defaultMybatisProviderInstance());

    // add method:
    // MybatisProvider getMybatisProvider() {
    //     return this$MybatisProvider
    // }
    injectMethod(
        declaringClass,
        new MethodNode(
            METHOD_GET_MYBATIS_PROVIDER,
            ACC_PUBLIC,
            MYBATIS_PROVIDER_CNODE,
            Parameter.EMPTY_ARRAY,
            NO_EXCEPTIONS,
            returns(field(providerField))));

    // add method:
    // void setMybatisProvider(MybatisProvider provider) {
    //     this$MybatisProvider = provider ?: DefaultMybatisProvider.instance
    // }
    injectMethod(
        declaringClass,
        new MethodNode(
            METHOD_SET_MYBATIS_PROVIDER,
            ACC_PUBLIC,
            ClassHelper.VOID_TYPE,
            params(param(MYBATIS_PROVIDER_CNODE, PROVIDER)),
            NO_EXCEPTIONS,
            block(
                ifs_no_return(
                    cmp(var(PROVIDER), ConstantExpression.NULL),
                    assigns(field(providerField), defaultMybatisProviderInstance()),
                    assigns(field(providerField), var(PROVIDER))))));

    for (MethodNode method : MYBATIS_CONTRIBUTION_HANDLER_CNODE.getMethods()) {
      if (Arrays.binarySearch(DELEGATING_METHODS, method.getName()) < 0) continue;
      List<Expression> variables = new ArrayList<Expression>();
      Parameter[] parameters = new Parameter[method.getParameters().length];
      for (int i = 0; i < method.getParameters().length; i++) {
        Parameter p = method.getParameters()[i];
        parameters[i] = new Parameter(makeClassSafe(p.getType()), p.getName());
        parameters[i].getType().setGenericsTypes(p.getType().getGenericsTypes());
        variables.add(var(p.getName()));
      }
      ClassNode returnType = makeClassSafe(method.getReturnType());
      returnType.setGenericsTypes(method.getReturnType().getGenericsTypes());
      returnType.setGenericsPlaceHolder(method.getReturnType().isGenericsPlaceHolder());

      MethodNode newMethod =
          new MethodNode(
              method.getName(),
              ACC_PUBLIC,
              returnType,
              parameters,
              NO_EXCEPTIONS,
              returns(call(field(providerField), method.getName(), args(variables))));
      newMethod.setGenericsTypes(method.getGenericsTypes());
      injectMethod(declaringClass, newMethod);
    }
  }