private JCTree makeCallMethod(List<JCStatement> body, int numParams) {
    MethodDefinitionBuilder callMethod = MethodDefinitionBuilder.callable(gen);
    callMethod.isOverride(true);
    callMethod.modifiers(Flags.PUBLIC);
    ProducedType returnType = gen.getReturnTypeOfCallable(typeModel);
    callMethod.resultType(gen.makeJavaType(returnType, JT_NO_PRIMITIVES), null);
    // Now append formal parameters
    switch (numParams) {
      case 3:
        callMethod.parameter(makeCallableCallParam(0, numParams - 3));
        // fall through
      case 2:
        callMethod.parameter(makeCallableCallParam(0, numParams - 2));
        // fall through
      case 1:
        callMethod.parameter(makeCallableCallParam(0, numParams - 1));
        break;
      case 0:
        break;
      default: // use varargs
        callMethod.parameter(makeCallableCallParam(Flags.VARARGS, 0));
    }

    // Return the call result, or null if a void method
    callMethod.body(body);
    return callMethod.build();
  }
  public JCNewClass build() {
    // Generate a subclass of Callable
    ListBuffer<JCTree> classBody = new ListBuffer<JCTree>();
    int numParams = paramLists.getParameters().size();
    int minimumParams = 0;
    for (Parameter p : paramLists.getParameters()) {
      if (p.isDefaulted() || p.isSequenced()) break;
      minimumParams++;
    }
    boolean isVariadic = minimumParams != numParams;
    if (parameterListTree != null) {
      // generate a method for each defaulted param
      for (Tree.Parameter p : parameterListTree.getParameters()) {
        if (p.getDefaultArgument() != null || p.getDeclarationModel().isSequenced()) {
          MethodDefinitionBuilder methodBuilder =
              gen.classGen().makeParamDefaultValueMethod(false, null, parameterListTree, p);
          classBody.append(methodBuilder.build());
        }
      }
    }

    // collect each parameter type from the callable type model rather than the declarations to get
    // them all bound
    java.util.List<ProducedType> parameterTypes = new ArrayList<ProducedType>(numParams);
    if (forwardCallTo != null) {
      for (int i = 0; i < numParams; i++)
        parameterTypes.add(gen.getParameterTypeOfCallable(typeModel, i));
    } else {
      // get them from our declaration
      for (Parameter p : paramLists.getParameters()) parameterTypes.add(p.getType());
    }

    // now generate a method for each supported minimum number of parameters below 4
    // which delegates to the $call$typed method if required
    for (int i = minimumParams, max = Math.min(numParams, 4); i < max; i++) {
      classBody.append(makeDefaultedCall(i, isVariadic, parameterTypes));
    }
    // generate the $call method for the max number of parameters,
    // which delegates to the $call$typed method if required
    classBody.append(makeDefaultedCall(numParams, isVariadic, parameterTypes));
    // generate the $call$typed method if required
    if (isVariadic && forwardCallTo == null)
      classBody.append(makeCallTypedMethod(body, parameterTypes));

    JCClassDecl classDef =
        gen.make().AnonymousClassDef(gen.make().Modifiers(0), classBody.toList());

    JCNewClass instance =
        gen.make()
            .NewClass(
                null,
                null,
                gen.makeJavaType(typeModel, JT_EXTENDS | JT_CLASS_NEW),
                List.<JCExpression>of(gen.make().Literal(typeModel.getProducedTypeName(true))),
                classDef);
    return instance;
  }
  /**
   * Appends to <tt>defs</tt> the definitions that would go into the class generated by {@link
   * #build()}
   *
   * @param defs a {@link ListBuffer} to which the definitions will be appended.
   */
  public void appendDefinitionsTo(ListBuffer<JCTree> defs) {
    if (hasField) {
      defs.append(generateField());
      if (variableInit != null) defs.append(generateFieldInit());
    }

    if (readable) {
      getterBuilder.modifiers(getGetSetModifiers());
      getterBuilder.noAnnotations(noAnnotations);
      defs.append(getterBuilder.build());
    }

    if (writable) {
      setterBuilder.modifiers(getGetSetModifiers());
      setterBuilder.noAnnotations(noAnnotations);
      defs.append(setterBuilder.build());
    }
  }
 private JCTree makeCallTypedMethod(
     List<JCStatement> body, java.util.List<ProducedType> parameterTypes) {
   // make the method
   MethodDefinitionBuilder methodBuilder =
       MethodDefinitionBuilder.method(gen, false, Naming.getCallableTypedMethodName());
   methodBuilder.modifiers(Flags.PRIVATE);
   ProducedType returnType = gen.getReturnTypeOfCallable(typeModel);
   methodBuilder.resultType(gen.makeJavaType(returnType, JT_NO_PRIMITIVES), null);
   // add all parameters
   int i = 0;
   for (Parameter param : paramLists.getParameters()) {
     ParameterDefinitionBuilder parameterBuilder =
         ParameterDefinitionBuilder.instance(gen, param.getName());
     JCExpression paramType = gen.makeJavaType(parameterTypes.get(i));
     parameterBuilder.type(paramType, null);
     methodBuilder.parameter(parameterBuilder);
     i++;
   }
   // Return the call result, or null if a void method
   methodBuilder.body(body);
   return methodBuilder.build();
 }