Firstly, your own solution cannot work because it contains a syntax error: !withincode(call(int MyClass.foo(int)))
does not compile because withincode()
does not take a pointcut parameter, only a signature. So it should really be: !withincode(int MyClass.foo(int))
.
Secondly, I think that what you really want is similar to (but not quite exactly) your initial solution because the solution from your own answer only works for direct recursion, not indirect recursion. Here is an example:
Driver application:
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;
}
}
As you can see, factorial(int)
calculates the factorial by direct recursion whereas factorial_indirect(int)
does so via indirect recursion because it calls helper(int)
which in turn calls the original method again. I am going to present an aspect which works for both situations, only blocking the initial call, no directly or indirectly recursive ones.
Aspect:
The original pointcut from your question was almost correct, it just should have used cflowbelow()
instead of cflow()
.
Please note that I am not really throwing exceptions but only logging them for demo purposes so as not to interrupt the program flow.
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"));
}
}
}
Console log:
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
As you can see my test condition logs errors for initial values of 0 and 10 in both direct and indirect recursion cases. Now if we switch to initialFactorialCall2(i)
in the before()
advice, the log for the indirect case changes to:
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
Please note the wrong reaction for 11!
where an exception is also logged for the internal call of factorial_indirect(10)
. This is clearly wrong, so you want to go with the cflowbelow()
solution instead.