Permalink
Browse files

Add `detectRebuild` option to mark rebuilt leaf nodes after each phase:

This can be used to track down cases where the AST is rebuilt despite
not containing any changes (i.e. it is `==` but not `eq` after a phase).
  • Loading branch information...
szeiger committed Aug 14, 2015
1 parent ed2af9b commit a71aff4bfff9ddef7a76e83485fb985b79a7099d
@@ -3,6 +3,7 @@ slick {
unicodeDump = true
sqlIndent = true
verifyTypes = true
detectRebuild = true
}
tsql {
@@ -15,6 +15,7 @@ object CompilerBenchmark {
def main(args: Array[String]) {
System.setProperty("slick.verifyTypes", "false")
System.setProperty("slick.detectRebuild", "false")
println("Number of queries: "+allQueries.length)
val phases = H2Driver.queryCompiler.phases
@@ -14,6 +14,9 @@ slick {
# Verify types after each query compiler phase
verifyTypes = false
# Detect unnecessary rebuilding of the AST after every query compiler phase
detectRebuild = false
}
slick.driver.MySQL {
@@ -469,7 +469,7 @@ final case class Ref(sym: TermSymbol) extends NullaryNode {
def withInferredType(scope: Type.Scope, typeChildren: Boolean): Self =
if(hasType) this else {
scope.get(sym) match {
case Some(t) => if(t == nodeType) this else copy() :@ t
case Some(t) => this :@ t
case _ => throw new SlickException("No type for symbol "+sym+" found for "+this)
}
}
@@ -14,7 +14,7 @@ class ForceOuterBinds extends Phase {
def apply(n: Node): Node = {
val t = n.nodeType.structuralRec
val n2 =
if(t != UnassignedType && !t.isInstanceOf[CollectionType]) First(wrap(Pure(n)))
if(!t.isInstanceOf[CollectionType]) First(wrap(Pure(n)))
else wrap(n)
n2.infer(typeChildren = true)
}
@@ -2,7 +2,7 @@ package slick.compiler
import scala.collection.immutable.HashMap
import slick.SlickException
import slick.util.{GlobalConfig, SlickLogger, Logging}
import slick.util._
import slick.ast.{SymbolNamer, Node}
import org.slf4j.LoggerFactory
@@ -78,13 +78,25 @@ class QueryCompiler(val phases: Vector[Phase]) extends Logging {
protected[this] def runPhase(p: Phase, state: CompilerState): CompilerState = state.symbolNamer.use {
val s2 = p(state)
if(s2.tree ne state.tree) {
logger.debug("After phase "+p.name+":", s2.tree)
if(GlobalConfig.detectRebuild && s2.tree == state.tree) {
val rebuilt = detectRebuiltLeafs(state.tree, s2.tree)
logger.debug("After phase "+p.name+": (no change but not identical)", s2.tree, (d => rebuilt.contains(RefId(d))))
} else
logger.debug("After phase "+p.name+":", s2.tree)
if(GlobalConfig.verifyTypes && s2.wellTyped)
(new VerifyTypes(after = Some(p))).apply(s2)
}
else logger.debug("After phase "+p.name+": (no change)")
s2
}
protected[this] def detectRebuiltLeafs(n1: Node, n2: Node): Set[RefId[Dumpable]] = {
if(n1 eq n2) Set.empty else {
val chres =
(n1.children, n2.children).zipped.map(detectRebuiltLeafs).foldLeft(Set.empty[RefId[Dumpable]])(_ ++ _)
if(chres.isEmpty) Set(RefId(n2)) else chres
}
}
}
object QueryCompiler {
@@ -33,6 +33,9 @@ object GlobalConfig {
/** Verify types after every query compiler phase */
val verifyTypes = config.getBooleanOr("slick.verifyTypes", false)
/** Detect unnecessary rebuilding of the AST after every query compiler phase */
val detectRebuild = config.getBooleanOr("slick.detectRebuild", false)
/** Get a `Config` object for a Slick driver */
def driverConfig(name: String): Config = {
val path = "slick.driver." + name
@@ -8,6 +8,10 @@ final class SlickLogger(val slf4jLogger: Slf4jLogger) {
@inline
def debug(msg: => String, n: => Dumpable): Unit = debug(msg+"\n"+SlickLogger.treePrinter.get(n))
@inline
def debug(msg: => String, n: => Dumpable, mark: (Dumpable => Boolean)): Unit =
debug(msg+"\n"+SlickLogger.treePrinter.copy(mark = mark).get(n))
@inline
def isDebugEnabled = slf4jLogger.isDebugEnabled()

0 comments on commit a71aff4

Please sign in to comment.