private void changeBaseScriptType(
      final AnnotatedNode parent, final ClassNode cNode, final ClassNode baseScriptType) {
    if (!cNode.isScriptBody()) {
      addError("Annotation " + MY_TYPE_NAME + " can only be used within a Script.", parent);
      return;
    }

    if (!baseScriptType.isScript()) {
      addError(
          "Declared type " + baseScriptType + " does not extend groovy.lang.Script class!", parent);
      return;
    }

    cNode.setSuperClass(baseScriptType);

    // Method in base script that will contain the script body code.
    MethodNode runScriptMethod = ClassHelper.findSAM(baseScriptType);

    // If they want to use a name other than than "run", then make the change.
    if (isCustomScriptBodyMethod(runScriptMethod)) {
      MethodNode defaultMethod = cNode.getDeclaredMethod("run", Parameter.EMPTY_ARRAY);
      // GROOVY-6706: Sometimes an NPE is thrown here.
      // The reason is that our transform is getting called more than once sometimes.
      if (defaultMethod != null) {
        cNode.removeMethod(defaultMethod);
        MethodNode methodNode =
            new MethodNode(
                runScriptMethod.getName(),
                runScriptMethod.getModifiers() & ~ACC_ABSTRACT,
                runScriptMethod.getReturnType(),
                runScriptMethod.getParameters(),
                runScriptMethod.getExceptions(),
                defaultMethod.getCode());
        // The AST node metadata has the flag that indicates that this method is a script body.
        // It may also be carrying data for other AST transforms.
        methodNode.copyNodeMetaData(defaultMethod);
        cNode.addMethod(methodNode);
      }
    }

    // If the new script base class does not have a contextual constructor (g.l.Binding), then we
    // won't either.
    // We have to do things this way (and rely on just default constructors) because the logic that
    // generates
    // the constructors for our script class have already run.
    if (cNode.getSuperClass().getDeclaredConstructor(CONTEXT_CTOR_PARAMETERS) == null) {
      ConstructorNode orphanedConstructor = cNode.getDeclaredConstructor(CONTEXT_CTOR_PARAMETERS);
      cNode.removeConstructor(orphanedConstructor);
    }
  }
  @Override
  public List<MethodNode> handleMissingMethod(
      final ClassNode receiver,
      final String name,
      final ArgumentListExpression argumentList,
      final ClassNode[] argumentTypes,
      final MethodCall call) {
    String[] decomposed = Traits.decomposeSuperCallName(name);
    if (decomposed != null) {
      return convertToDynamicCall(call, receiver, decomposed, argumentTypes);
    }
    if (call instanceof MethodCallExpression) {
      MethodCallExpression mce = (MethodCallExpression) call;
      if (mce.getReceiver() instanceof VariableExpression) {
        VariableExpression var = (VariableExpression) mce.getReceiver();

        // GROOVY-7322
        // static method call in trait?
        ClassNode type = null;
        if (isStaticTraitReceiver(receiver, var)) {
          type = receiver.getGenericsTypes()[0].getType();
        } else if (isThisTraitReceiver(var)) {
          type = receiver;
        }
        if (type != null && Traits.isTrait(type)) {
          ClassNode helper = Traits.findHelper(type);
          Parameter[] params = new Parameter[argumentTypes.length + 1];
          params[0] = new Parameter(ClassHelper.CLASS_Type.getPlainNodeReference(), "staticSelf");
          for (int i = 1; i < params.length; i++) {
            params[i] = new Parameter(argumentTypes[i - 1], "p" + i);
          }
          MethodNode method = helper.getDeclaredMethod(name, params);
          if (method != null) {
            return Collections.singletonList(makeDynamic(call, method.getReturnType()));
          }
        }
      }

      ClassNode dynamic = mce.getNodeMetaData(TraitASTTransformation.DO_DYNAMIC);
      if (dynamic != null) {
        return Collections.singletonList(makeDynamic(call, dynamic));
      }
    }
    return NOTFOUND;
  }
Esempio n. 3
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;
  }
Esempio n. 4
0
 /**
  * Tests whether the ClasNode implements the specified method name.
  *
  * @param classNode The ClassNode
  * @param methodName The method name
  * @return True if it does implement the method
  */
 public static boolean implementsZeroArgMethod(ClassNode classNode, String methodName) {
   MethodNode method = classNode.getDeclaredMethod(methodName, Parameter.EMPTY_ARRAY);
   return method != null && (method.isPublic() || method.isProtected()) && !method.isAbstract();
 }