I am writing a macro which wraps any annotated method with boilerplate code.
Usage:
@Instrument
@def executeQuery(a: String, startTimestamp: Long, endTimestamp: Long): Unit = {
}
Macro implementation:
def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Tree = {
import c.universe._
val inputs = annottees.map(_.tree).toList
val enclosingClass = c.internal.enclosingOwner.name.decodedName.toString
// Extract the tags from the annotation
val tags = c.prefix.tree match {
case q"new $name(..$params)" => params.map(_.toString())
case _ => Nil
}
val outputTrees = inputs.flatMap {
// Capture the method definition and using quasiquotes wrap it with the instrumentation code
// before and after the method execution.
// Captures pattern like (modifier) def methodName[TypeParams](params): ReturnType = { body }
case q"$mods def $methodName[..$tparams](...$params): $returnType = $body" =>
val instrumented =
q"""
$mods def $methodName[..$tparams](...$params): $returnType = {
// log
val result = $body
// log
result
}
"""
instrumented :: Nil
case q"$x" =>
c.abort(
c.enclosingPosition,
s"Annotation @InstrumentMethod can only be applied to methods, but applied to $x")
}
// Return the modified method Tree with instrumentation.
outputTrees.last
}
The value being passed to “start” to the re-written code is incorrect, while the rest of the arguments are correct.
If I am calling
executeQuery("url", 1716505342, 1716505342)
gets
executeQuery("url", 1351325935683708, 1716505342)
The 2nd argument is incorrect, however the rest looks alright.