From d9859036ed9f4e2905583ba0c31fd14c756b53d8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 13:29:16 +0200 Subject: [PATCH 1/5] Cache classloaders in Splicer --- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 73876125afd3..e90b70a384f8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -93,6 +93,8 @@ object Splicer { } } + private val classLoaders = scala.collection.mutable.HashMap.empty[String, URLClassLoader] + /** Tree interpreter that can interpret calls to static methods with it's default arguments * * The interpreter assumes that all calls in the trees are to code that was @@ -101,8 +103,12 @@ object Splicer { private class Interpreter(pos: Position)(implicit ctx: Context) { private[this] val classLoader = { - val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - new URLClassLoader(urls, getClass.getClassLoader) + val cp = ctx.settings.classpath.value + def newClassLoader = { + val urls = cp.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + new URLClassLoader(urls, getClass.getClassLoader) + } + classLoaders.getOrElseUpdate(cp, newClassLoader) } /** Returns the interpreted result of interpreting the code a call to the symbol with default arguments. From 04c18a570a04b82471832bd1ded7faba2d77a587 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 16:28:23 +0200 Subject: [PATCH 2/5] Make splicer only valid for this run --- .../dotty/tools/dotc/transform/ReifyQuotes.scala | 4 +++- .../src/dotty/tools/dotc/transform/Splicer.scala | 16 ++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index b6e448e2e1fd..f6ab3abc8b84 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -88,6 +88,8 @@ import dotty.tools.dotc.core.quoted._ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { import ast.tpd._ + private lazy val splicer: Splicer = new Splicer + override def phaseName: String = "reifyQuotes" override def run(implicit ctx: Context): Unit = @@ -542,7 +544,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { // Simplification of the call done in PostTyper for non-macros can also be performed now // see PostTyper `case Inlined(...) =>` for description of the simplification val call2 = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - val spliced = Splicer.splice(body, call, bindings, tree.pos).withPos(tree.pos) + val spliced = splicer.splice(body, call, bindings, tree.pos).withPos(tree.pos) transform(cpy.Inlined(tree)(call2, bindings, spliced)) } else super.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index e90b70a384f8..14da8ccb2d3b 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -21,9 +21,11 @@ import dotty.tools.dotc.util.Positions.Position import scala.reflect.ClassTag /** Utility class to splice quoted expressions */ -object Splicer { +class Splicer { import tpd._ + private var classLoader: URLClassLoader = _ + /** Splice the Tree for a Quoted expression. `~'(xyz)` becomes `xyz` * and for `~xyz` the tree of `xyz` is interpreted for which the * resulting expression is returned as a `Tree` @@ -93,8 +95,6 @@ object Splicer { } } - private val classLoaders = scala.collection.mutable.HashMap.empty[String, URLClassLoader] - /** Tree interpreter that can interpret calls to static methods with it's default arguments * * The interpreter assumes that all calls in the trees are to code that was @@ -102,13 +102,9 @@ object Splicer { */ private class Interpreter(pos: Position)(implicit ctx: Context) { - private[this] val classLoader = { - val cp = ctx.settings.classpath.value - def newClassLoader = { - val urls = cp.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - new URLClassLoader(urls, getClass.getClassLoader) - } - classLoaders.getOrElseUpdate(cp, newClassLoader) + if (classLoader == null) { + val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + classLoader = new URLClassLoader(urls, getClass.getClassLoader) } /** Returns the interpreted result of interpreting the code a call to the symbol with default arguments. From 0d1243a29746f4e2dab41da092c0aee68dfa652a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 17:36:11 +0200 Subject: [PATCH 3/5] Cache classloader directly in ReifyQuotes --- .../tools/dotc/transform/ReifyQuotes.scala | 13 ++++++++-- .../dotty/tools/dotc/transform/Splicer.scala | 26 ++++++------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index f6ab3abc8b84..d672ff746d75 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -88,7 +88,8 @@ import dotty.tools.dotc.core.quoted._ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { import ast.tpd._ - private lazy val splicer: Splicer = new Splicer + /** Classloader used for loading macros */ + private var macroClassLoader: java.lang.ClassLoader = _ override def phaseName: String = "reifyQuotes" @@ -544,7 +545,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { // Simplification of the call done in PostTyper for non-macros can also be performed now // see PostTyper `case Inlined(...) =>` for description of the simplification val call2 = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - val spliced = splicer.splice(body, call, bindings, tree.pos).withPos(tree.pos) + val spliced = Splicer.splice(body, call, bindings, tree.pos, getMacroClassLoader).withPos(tree.pos) transform(cpy.Inlined(tree)(call2, bindings, spliced)) } else super.transform(tree) @@ -622,6 +623,14 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { transform(tp) } + private def getMacroClassLoader(implicit ctx: Context): ClassLoader = { + if (macroClassLoader == null) { + val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + macroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) + } + macroClassLoader + } + override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.is(Macro) /** Returns the type of the compiled macro as a lambda: Seq[Any] => Object */ diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 14da8ccb2d3b..1d9acc0324d7 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -21,27 +21,22 @@ import dotty.tools.dotc.util.Positions.Position import scala.reflect.ClassTag /** Utility class to splice quoted expressions */ -class Splicer { +object Splicer { import tpd._ - private var classLoader: URLClassLoader = _ - /** Splice the Tree for a Quoted expression. `~'(xyz)` becomes `xyz` * and for `~xyz` the tree of `xyz` is interpreted for which the * resulting expression is returned as a `Tree` * * See: `ReifyQuotes` */ - def splice(tree: Tree, call: Tree, bindings: List[Tree], pos: Position)(implicit ctx: Context): Tree = tree match { + def splice(tree: Tree, call: Tree, bindings: List[Tree], pos: Position, classLoader: ClassLoader)(implicit ctx: Context): Tree = tree match { case Quoted(quotedTree) => quotedTree - case _ => reflectiveSplice(tree, call, bindings, pos) - } - - private def reflectiveSplice(tree: Tree, call: Tree, bindings: List[Tree], pos: Position)(implicit ctx: Context): Tree = { - val liftedArgs = getLiftedArgs(call, bindings) - val interpreter = new Interpreter(pos) - val interpreted = interpreter.interpretCallToSymbol[Seq[Any] => Object](call.symbol) - interpreted.flatMap(lambda => evaluateLambda(lambda, liftedArgs, pos)).fold(tree)(PickledQuotes.quotedExprToTree) + case _ => + val liftedArgs = getLiftedArgs(call, bindings) + val interpreter = new Interpreter(pos, classLoader) + val interpreted = interpreter.interpretCallToSymbol[Seq[Any] => Object](call.symbol) + interpreted.flatMap(lambda => evaluateLambda(lambda, liftedArgs, pos)).fold(tree)(PickledQuotes.quotedExprToTree) } /** Given the inline code and bindings, compute the lifted arguments that will be used to execute the macro @@ -100,12 +95,7 @@ class Splicer { * The interpreter assumes that all calls in the trees are to code that was * previously compiled and is present in the classpath of the current context. */ - private class Interpreter(pos: Position)(implicit ctx: Context) { - - if (classLoader == null) { - val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - classLoader = new URLClassLoader(urls, getClass.getClassLoader) - } + private class Interpreter(pos: Position, classLoader: ClassLoader)(implicit ctx: Context) { /** Returns the interpreted result of interpreting the code a call to the symbol with default arguments. * Return Some of the result or None if some error happen during the interpretation. From 69383b0a270b08bf945593bcecd6cea4f2aed93f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 20:01:27 +0200 Subject: [PATCH 4/5] Add private[this] and rename field --- .../src/dotty/tools/dotc/transform/ReifyQuotes.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index d672ff746d75..2aa0ccd61a74 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -89,7 +89,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { import ast.tpd._ /** Classloader used for loading macros */ - private var macroClassLoader: java.lang.ClassLoader = _ + private[this] var myMacroClassLoader: java.lang.ClassLoader = _ override def phaseName: String = "reifyQuotes" @@ -545,7 +545,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { // Simplification of the call done in PostTyper for non-macros can also be performed now // see PostTyper `case Inlined(...) =>` for description of the simplification val call2 = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - val spliced = Splicer.splice(body, call, bindings, tree.pos, getMacroClassLoader).withPos(tree.pos) + val spliced = Splicer.splice(body, call, bindings, tree.pos, macroClassLoader).withPos(tree.pos) transform(cpy.Inlined(tree)(call2, bindings, spliced)) } else super.transform(tree) @@ -623,12 +623,12 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { transform(tp) } - private def getMacroClassLoader(implicit ctx: Context): ClassLoader = { - if (macroClassLoader == null) { + private def macroClassLoader(implicit ctx: Context): ClassLoader = { + if (myMacroClassLoader == null) { val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - macroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) + myMacroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) } - macroClassLoader + myMacroClassLoader } override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.is(Macro) From 5b7c02f38efdada2bf1ed123e666adb42e825d7e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 4 Apr 2018 08:03:28 +0200 Subject: [PATCH 5/5] Move method --- .../dotty/tools/dotc/transform/ReifyQuotes.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 2aa0ccd61a74..3b498d58da35 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -90,6 +90,13 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { /** Classloader used for loading macros */ private[this] var myMacroClassLoader: java.lang.ClassLoader = _ + private def macroClassLoader(implicit ctx: Context): ClassLoader = { + if (myMacroClassLoader == null) { + val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + myMacroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) + } + myMacroClassLoader + } override def phaseName: String = "reifyQuotes" @@ -623,14 +630,6 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { transform(tp) } - private def macroClassLoader(implicit ctx: Context): ClassLoader = { - if (myMacroClassLoader == null) { - val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - myMacroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) - } - myMacroClassLoader - } - override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.is(Macro) /** Returns the type of the compiled macro as a lambda: Seq[Any] => Object */