使用AspectJ验证初始调用递归函数的参数

时间:2016-04-19 15:06:21

标签: java aspectj

假设我有以下递归函数

public class MyClass{
    public int foo(int arg){
        ...
    }
}

如果arg的初始值为10,那么我想在一个方面抛出一个异常(之后它可以接受)。我是AspectJ的新手,但想出了以下似乎无法正常工作的内容。

public aspect CheckBounds{
    pointcut initialCall(int x):
        call(int MyClass.foo(int))
        && !cflow(call(int MyClass.foo(int)))
        && args(x);

    before(int x) : initialCall(x){
        if(x == 10){
            throw new IllegalArgumentException("x must not be 10");
        }
    }
}

您有任何建议/推荐的方法来实现这一目标吗?

2 个答案:

答案 0 :(得分:1)

首先,您自己的解决方案无法正常工作,因为它包含语法错误:!withincode(call(int MyClass.foo(int)))无法编译,因为withincode()不接受切入点参数,只接受签名。所以它应该是:!withincode(int MyClass.foo(int))

其次,我认为您真正想要的是与您的初始解决方案类似(但不完全相同),因为您自己的答案中的解决方案仅适用于直接递归,而不适用于间接递归。这是一个例子:

驱动程序应用程序:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        Application application = new Application();
        System.out.println("Directly recursive factorials:");
        for (int i = 0; i < 12; i++)
            System.out.printf("%2d! = %10d%n", i, application.factorial(i));
        System.out.println("\nIndirectly recursive factorials:");
        for (int i = 0; i < 12; i++)
            System.out.printf("%2d! = %10d%n", i, application.factorial_indirect(i));
    }

    public int factorial(int i) {
        return i > 1 ? i * factorial(i - 1) : 1;
    }

    public int factorial_indirect(int i) {
        return helper(i);
    }

    public int helper(int i) {
        return i > 1 ? i * factorial_indirect(i - 1) : 1;
    }
}

如您所见,factorial(int)通过直接递归计算阶乘,而factorial_indirect(int)通过间接递归计算,因为它调用helper(int),而cflowbelow()又调用原始方法。我将提出一个适用于这两种情况的方面,仅阻止初始调用,不直接或间接递归调用。

<强>方面:

您问题的原始切入点几乎是正确的,它应该使用cflow()代替package de.scrum_master.aspect; import de.scrum_master.app.Application; public aspect CheckBounds { pointcut factorialCall() : call(int Application.factorial*(int)); pointcut initialFactorialCall(int i) : factorialCall() && !cflowbelow(factorialCall()) && args(i); pointcut initialFactorialCall2(int i) : factorialCall() && !withincode(int Application.factorial*(int)) && args(i); before(int i) : initialFactorialCall(i) { if (i < 1 || i == 10) { System.out.println(new IllegalArgumentException("x must be >=1 and != 10")); } } }

请注意,我并没有真正抛出异常,只是为了演示目的而将它们记录下来,以免中断程序流程。

Directly recursive factorials:
java.lang.IllegalArgumentException: x must be >=1 and != 10
 0! =          1
 1! =          1
 2! =          2
 3! =          6
 4! =         24
 5! =        120
 6! =        720
 7! =       5040
 8! =      40320
 9! =     362880
java.lang.IllegalArgumentException: x must be >=1 and != 10
10! =    3628800
11! =   39916800

Indirectly recursive factorials:
java.lang.IllegalArgumentException: x must be >=1 and != 10
 0! =          1
 1! =          1
 2! =          2
 3! =          6
 4! =         24
 5! =        120
 6! =        720
 7! =       5040
 8! =      40320
 9! =     362880
java.lang.IllegalArgumentException: x must be >=1 and != 10
10! =    3628800
11! =   39916800

控制台日志:

initialFactorialCall2(i)

正如您所看到的,我的测试条件在直接和间接递归情况下记录初始值0和10的错误。现在,如果我们在before()建议中切换到Indirectly recursive factorials: java.lang.IllegalArgumentException: x must be >=1 and != 10 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 java.lang.IllegalArgumentException: x must be >=1 and != 10 10! = 3628800 java.lang.IllegalArgumentException: x must be >=1 and != 10 11! = 39916800 ,则间接案例的日志将更改为:

11!

请注意factorial_indirect(10)的错误反应,其中还记录了cflowbelow()内部调用的异常。这显然是错误的,因此您希望改为使用payx()解决方案。

答案 1 :(得分:0)

事实证明我使用了错误的切入点。我的目的是确保foo()的调用不是在foo中完成的(只是直接调用)。关于改变&#c; cflow&#39;来自内部代码&#39;该建议现在按预期运作:

public aspect CheckBounds{
    pointcut initialCall(int x):
        call(int MyClass.foo(int))
        && !withincode(int MyClass.foo(int))
        && args(x);

    before(int x) : initialCall(x){
        if(x == 10){
            throw new IllegalArgumentException("x must not be 10");
        }
    }
}