ProceedingJoinPointGenerator.generateProceedingJoinPoint may be called
in each iteration of aspectKContext.aspectLookUp[target],
generateProceedingJoinPoint method parameter should be added 'innerProceed'
which contains advice body of around, after annotation.
if innerProceed parameter exists, the result of generateProceedingJoinPoint
may be added to input innerProceed such as.
@Target
fun doSomething(arg1:String):String {
fun $doSomething(arg1:String = arg1):String {
println("hello aspectk")
return ""
}
return SomeAspect.doAround1(
ProceedingJoinPoint(...) { args:List<Any?> ->
SomeAspect.doAround2(
ProceedingJoinPoint(...) { args:List<Any?> ->
try {
$doSomething(args[0] as String)
} catch (e:Exception) {
throw e
} finally {
SomeAspect.doAfter1(JoinPoint(...))
}
}
)
}
)
}
this is only raw draft. so it may not be correct.
Design rationale — @after placement (innermost):
@after is placed in the finally block that wraps only $doSomething (the original function
body), not the entire @around chain. This is intentional:
-
@after's contract is "execute after the target function", not "execute after all aspects".
If an @around advice throws before calling pjp.proceed(), the original function never
ran, so @after should not fire in that case.
-
@around advice is responsible for handling its own exceptions internally.
Wrapping the outer @around call with the finally block would mean @after fires even
when @around itself fails — which conflates two unrelated concerns.
-
This design gives predictable execution order control after the original code runs:
@after fires first (innermost finally), then @around's post-proceed logic runs
outward. The order is deterministic and mirrors the lexical nesting of the generated IR.
ProceedingJoinPointGenerator.generateProceedingJoinPoint may be called
in each iteration of aspectKContext.aspectLookUp[target],
generateProceedingJoinPoint method parameter should be added 'innerProceed'
which contains advice body of around, after annotation.
if innerProceed parameter exists, the result of generateProceedingJoinPoint
may be added to input innerProceed such as.
this is only raw draft. so it may not be correct.
Design rationale — @after placement (innermost):
@after is placed in the finally block that wraps only $doSomething (the original function
body), not the entire @around chain. This is intentional:
@after's contract is "execute after the target function", not "execute after all aspects".
If an @around advice throws before calling pjp.proceed(), the original function never
ran, so @after should not fire in that case.
@around advice is responsible for handling its own exceptions internally.
Wrapping the outer @around call with the finally block would mean @after fires even
when @around itself fails — which conflates two unrelated concerns.
This design gives predictable execution order control after the original code runs:
@after fires first (innermost finally), then @around's post-proceed logic runs
outward. The order is deterministic and mirrors the lexical nesting of the generated IR.