Skip to content

Commit

Permalink
Add @performance annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
propensive committed Nov 10, 2020
1 parent fe499bf commit 89dd7a8
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .fury/config
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
#
# For more information, please visit https://propensive.com/fury/
#
layerRef QmVSiRso821bTTzBPqy37v2JbGSroDNwNcJMMV1YN3jc46
layerRef QmVvt9GRoEcLA6WL4RCpxraSSXiCnvUvq5chBvDazuNdmF
# vim: set noai ts=12 sw=12:
Binary file modified .fury/layers.db
Binary file not shown.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ lazy val core = (project in file(".core"))
.settings(
name := "magnolia-core",
Compile / scalaSource := baseDirectory.value / ".." / "src" / "core",
libraryDependencies += "com.propensive" %% "probably-cli" % "0.5.0",
libraryDependencies += "com.propensive" %% "mercator" % "0.2.1"
)

Expand Down
2 changes: 2 additions & 0 deletions src/core/interface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,8 @@ final case class TypeName(owner: String, short: String, typeArguments: Seq[TypeN
*/
final class debug(typeNamePart: String = "") extends scala.annotation.StaticAnnotation

final class performance() extends scala.annotation.StaticAnnotation

private[magnolia] final case class EarlyExit[E](e: E) extends Exception with util.control.NoStackTrace

object MagnoliaUtil {
Expand Down
59 changes: 46 additions & 13 deletions src/core/magnolia.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package magnolia

import probably._

import scala.annotation.compileTimeOnly
import scala.collection.mutable
import scala.language.existentials
Expand Down Expand Up @@ -82,7 +84,7 @@ object Magnolia {
* will suffice, however the qualifications regarding additional type parameters and implicit
* parameters apply equally to `dispatch` as to `combine`.
* */
def gen[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = Stack.withContext(c) { stack =>
def gen[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = Stack.withContext[T](c) { stack =>
import c.internal._
import c.universe._
import definitions._
Expand Down Expand Up @@ -127,7 +129,7 @@ object Magnolia {
.flatMap(_.tree.children.tail.collectFirst {
case Literal(Constant(arg: String)) => arg
case tree if DebugTpe.companion.decls.exists(_ == tree.symbol) => "" // Default constructor, i.e. @debug or @debug()
case other => error(s"Invalid argument $other in @debug annotation. Only string literals or empty constructor supported")
case other => error(s"invalid argument $other in @debug annotation; only string literals or empty constructor supported")
})

object DeferredRef {
Expand Down Expand Up @@ -800,17 +802,18 @@ private[magnolia] object CompileTimeState {
private val threadLocalStack = ThreadLocal.withInitial[Stack[dummyContext.type]](() => new Stack[dummyContext.type])
private val threadLocalWorkSet = ThreadLocal.withInitial[mutable.Set[whitebox.Context#Symbol]](() => mutable.Set.empty)

def withContext(c: whitebox.Context)(fn: Stack[c.type] => c.Tree): c.Tree = {
val stack = threadLocalStack.get()
val workSet = threadLocalWorkSet.get()
workSet += c.macroApplication.symbol
val depth = c.enclosingMacros.count(m => workSet(m.macroApplication.symbol))
try fn(stack.asInstanceOf[Stack[c.type]])
finally if (depth <= 1) {
stack.clear()
workSet.clear()
}
}
def withContext[T: c.WeakTypeTag](c: whitebox.Context)(fn: Stack[c.type] => c.Tree): c.Tree =
Hook.test(c)(s"Deriving ${c.weakTypeOf[T]}") {
val stack = threadLocalStack.get()
val workSet = threadLocalWorkSet.get()
workSet += c.macroApplication.symbol
val depth = c.enclosingMacros.count(m => workSet(m.macroApplication.symbol))
try fn(stack.asInstanceOf[Stack[c.type]])
finally if (depth <= 1) {
stack.clear()
workSet.clear()
}
}.check(_ => true)
}
}

Expand All @@ -822,3 +825,33 @@ final class CallByNeed[+A](private[this] var eval: () => A) extends Serializable
result
}
}

object Hook {
private var postAction: () => Unit = null
private var runPerformance: Boolean = false

def test(c: blackbox.Context): Runner = {
import c.universe._

runPerformance ||= c.macroApplication.symbol.annotations.exists(_.tree.tpe <:< typeOf[performance])

val toCheck: mutable.ListBuffer[() => Unit] =
c.enclosingUnit.asInstanceOf[scala.tools.nsc.Global#CompilationUnit].toCheck

// Add an action to run once at the end, if it has not already been added
if(!toCheck.contains(postAction)) {
val action: () => Unit = { () => if(runPerformance) {
c.info(c.universe.NoPosition, "Performance report from Magnolia:", true)
c.info(c.universe.NoPosition, Suite.show(probably.global.test.report()), true)
probably.global.test.clear()
toCheck -= postAction
postAction = null
} }
toCheck += action
postAction = action
}

probably.global.test
}

}
1 change: 1 addition & 0 deletions src/examples/show.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ trait GenericShow[Out] {
}

/** bind the Magnolia macro to this derivation object */
@performance
implicit def gen[T]: Show[Out, T] = macro Magnolia.gen[T]
}

Expand Down
2 changes: 2 additions & 0 deletions src/test/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ import scala.annotation.StaticAnnotation
import scala.util.control.NonFatal

sealed trait Tree[+T]

case class Leaf[+L](value: L) extends Tree[L]
case class Branch[+B](left: Tree[B], right: Tree[B]) extends Tree[B]

sealed trait Path[+A]

case class Destination[+A](value: A) extends Path[A]
case class Crossroad[+A](left: Path[A], right: Path[A]) extends Path[A]
case class OffRoad[+A](path: Option[Path[A]]) extends Path[A]
Expand Down

0 comments on commit 89dd7a8

Please sign in to comment.