Skip to content
Permalink
Browse files

brings macros up to speed

Before reflection refactoring, macro contexts only exposed a mirror.
Now it's time to expose both a universe (the compiler instance) and
a mirror (a macro-specific symbol resolver).

By the way, speaking of mirrors. Macro contexts have their own mirror,
which is different from compiler's rootMirror. This is done because
macros need to be able to resolve stuff from empty package.

Reflection refactoring brought major changes to runtime evaluation,
which got dropped from universes and now requires scala-compiler.jar.
However there are macro users, who would like to do eval inside macros.
To help them we expose `libraryClassLoader` to manually build toolboxes,
and also a simple-to-use `c.eval` method.

I've also sneakily introduced `c.parse`, because it's something that
has also been frequently requested. Moreover, it might help Scaladoc.
So I decided that it might be worth it to add this new functionality.
  • Loading branch information
xeno-by committed Jun 8, 2012
1 parent bdff66e commit ce67870e64afabf75363679bcee597812ad223e9
Showing with 293 additions and 119 deletions.
  1. +1 −0 src/compiler/scala/reflect/internal/Definitions.scala
  2. +19 −15 src/compiler/scala/reflect/makro/runtime/Aliases.scala
  3. +4 −3 src/compiler/scala/reflect/makro/runtime/CapturedVariables.scala
  4. +8 −3 src/compiler/scala/reflect/makro/runtime/Context.scala
  5. +2 −1 src/compiler/scala/reflect/makro/runtime/Enclosures.scala
  6. +18 −0 src/compiler/scala/reflect/makro/runtime/Evals.scala
  7. +1 −1 src/compiler/scala/reflect/makro/runtime/Exprs.scala
  8. +11 −10 src/compiler/scala/reflect/makro/runtime/FrontEnds.scala
  9. +27 −9 src/compiler/scala/reflect/makro/runtime/Infrastructure.scala
  10. +40 −0 src/compiler/scala/reflect/makro/runtime/Mirrors.scala
  11. +2 −2 src/compiler/scala/reflect/makro/runtime/Names.scala
  12. +25 −0 src/compiler/scala/reflect/makro/runtime/Parsers.scala
  13. +4 −4 src/compiler/scala/reflect/makro/runtime/Reifiers.scala
  14. +3 −3 src/compiler/scala/reflect/makro/runtime/Settings.scala
  15. +1 −1 src/compiler/scala/reflect/makro/runtime/Traces.scala
  16. +2 −2 src/compiler/scala/reflect/makro/runtime/TypeTags.scala
  17. +20 −20 src/compiler/scala/reflect/makro/runtime/Typers.scala
  18. +1 −1 src/compiler/scala/reflect/makro/runtime/Util.scala
  19. +1 −1 src/compiler/scala/reflect/reify/Taggers.scala
  20. +15 −18 src/compiler/scala/tools/nsc/typechecker/Macros.scala
  21. +2 −2 src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
  22. +16 −18 src/library/scala/reflect/makro/Aliases.scala
  23. +7 −2 src/library/scala/reflect/makro/Context.scala
  24. +8 −0 src/library/scala/reflect/makro/Evals.scala
  25. +1 −1 src/library/scala/reflect/makro/FrontEnds.scala
  26. +29 −0 src/library/scala/reflect/makro/Infrastructure.scala
  27. +1 −1 src/library/scala/reflect/makro/Names.scala
  28. +17 −0 src/library/scala/reflect/makro/Parsers.scala
  29. +1 −1 src/library/scala/reflect/makro/Typers.scala
  30. +6 −0 src/library/scala/reflect/makro/package.scala
@@ -477,6 +477,7 @@ trait Definitions extends api.StandardDefinitions {
lazy val MacroContextClass = getClassIfDefined("scala.reflect.makro.Context") // defined in scala-reflect.jar, so we need to be careful
def MacroContextPrefix = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.prefix) else NoSymbol
def MacroContextPrefixType = if (MacroContextClass != NoSymbol) getMemberType(MacroContextClass, tpnme.PrefixType) else NoSymbol
def MacroContextUniverse = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.universe) else NoSymbol
def MacroContextMirror = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.mirror) else NoSymbol
def MacroContextReify = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.reify) else NoSymbol
lazy val MacroImplAnnotation = requiredClass[scala.reflect.makro.internal.macroImpl]
@@ -4,20 +4,24 @@ package runtime
trait Aliases {
self: Context =>

/** Aliases of mirror types */
override type Symbol = mirror.Symbol
override type Type = mirror.Type
override type Name = mirror.Name
override type Tree = mirror.Tree
// override type Position = mirror.Position
override type Scope = mirror.Scope
override type Modifiers = mirror.Modifiers
override type Expr[+T] = mirror.Expr[T]
override type TypeTag[T] = mirror.TypeTag[T]
override type ConcreteTypeTag[T] = mirror.ConcreteTypeTag[T]
override type Symbol = universe.Symbol
override type Type = universe.Type
override type Name = universe.Name
override type TermName = universe.TermName
override type TypeName = universe.TypeName
override type Tree = universe.Tree
// override type Position = universe.Position
override type Scope = universe.Scope
override type Modifiers = universe.Modifiers

/** Creator/extractor objects for Expr and TypeTag values */
override val TypeTag = mirror.TypeTag
override val ConcreteTypeTag = mirror.ConcreteTypeTag
override val Expr = mirror.Expr
override type Expr[+T] = universe.Expr[T]
override val Expr = universe.Expr

override type TypeTag[T] = universe.TypeTag[T]
override type ConcreteTypeTag[T] = universe.ConcreteTypeTag[T]
override val TypeTag = universe.TypeTag
override val ConcreteTypeTag = universe.ConcreteTypeTag
override def typeTag[T](implicit ttag: TypeTag[T]) = ttag
override def concreteTypeTag[T](implicit cttag: ConcreteTypeTag[T]) = cttag
override def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe
}
@@ -5,10 +5,11 @@ trait CapturedVariables {
self: Context =>

import mirror._
import universe._

def captureVariable(vble: Symbol): Unit = mirror.captureVariable(vble)
def captureVariable(vble: Symbol): Unit = universe.captureVariable(vble)

def referenceCapturedVariable(vble: Symbol): Tree = mirror.referenceCapturedVariable(vble)
def referenceCapturedVariable(vble: Symbol): Tree = universe.referenceCapturedVariable(vble)

def capturedVariableType(vble: Symbol): Type = mirror.capturedVariableType(vble)
def capturedVariableType(vble: Symbol): Type = universe.capturedVariableType(vble)
}
@@ -8,21 +8,26 @@ abstract class Context extends scala.reflect.makro.Context
with CapturedVariables
with Infrastructure
with Enclosures
with Mirrors
with Names
with Reifiers
with FrontEnds
with Settings
with Typers
with Parsers
with Exprs
with TypeTags
with Evals
with Util
with Traces {

val mirror: Global
val universe: Global

val callsiteTyper: mirror.analyzer.Typer
val mirror: MirrorOf[universe.type] = new ContextMirror

val callsiteTyper: universe.analyzer.Typer

val prefix: Expr[PrefixType]

val expandee: Tree
}
}
@@ -4,6 +4,7 @@ package runtime
trait Enclosures {
self: Context =>

import universe._
import mirror._

private def site = callsiteTyper.context
@@ -16,7 +17,7 @@ trait Enclosures {
val enclosingApplication: Tree = enclTrees collectFirst { case t: Apply => t } getOrElse EmptyTree
val enclosingClass: Tree = site.enclClass.tree
val enclosingImplicits: List[(Type, Tree)] = site.openImplicits
val enclosingMacros: List[Context] = this :: mirror.analyzer.openMacros // include self
val enclosingMacros: List[Context] = this :: universe.analyzer.openMacros // include self
val enclosingMethod: Tree = site.enclMethod.tree
val enclosingPosition: Position = if (enclPoses.isEmpty) NoPosition else enclPoses.head.pos
val enclosingUnit: CompilationUnit = currentRun.currentUnit
@@ -0,0 +1,18 @@
package scala.reflect.makro
package runtime

import scala.reflect.runtime.{universe => ru}
import scala.tools.reflect.ToolBox

trait Evals {
self: Context =>

private lazy val evalMirror = ru.runtimeMirror(libraryClassLoader)
private lazy val evalToolBox = evalMirror.mkToolBox()
private lazy val evalImporter = ru.mkImporter(universe).asInstanceOf[ru.Importer { val from: universe.type }]

def eval[T](expr: Expr[T]): T = {
val imported = evalImporter.importTree(expr.tree)
evalToolBox.runExpr(imported).asInstanceOf[T]
}
}
@@ -4,5 +4,5 @@ package runtime
trait Exprs {
self: Context =>

def Expr[T: TypeTag](tree: Tree): Expr[T] = mirror.Expr[T](mirror.rootMirror, mirror.FixedMirrorTreeCreator(mirror.rootMirror, tree))
def Expr[T: TypeTag](tree: Tree): Expr[T] = universe.Expr[T](mirror, universe.FixedMirrorTreeCreator(mirror, tree))
}
@@ -4,31 +4,32 @@ package runtime
trait FrontEnds extends scala.tools.reflect.FrontEnds {
self: Context =>

import universe._
import mirror._

override type Position = mirror.Position
override type Position = universe.Position

def frontEnd: FrontEnd = wrapReporter(mirror.reporter)
def frontEnd: FrontEnd = wrapReporter(universe.reporter)

def setFrontEnd(frontEnd: FrontEnd): this.type = {
mirror.reporter = wrapFrontEnd(frontEnd)
universe.reporter = wrapFrontEnd(frontEnd)
this
}

def withFrontEnd[T](frontEnd: FrontEnd)(op: => T): T = {
val old = mirror.reporter
val old = universe.reporter
setFrontEnd(frontEnd)
try op
finally mirror.reporter = old
finally universe.reporter = old
}

def echo(pos: Position, msg: String): Unit = mirror.reporter.echo(pos, msg)
def echo(pos: Position, msg: String): Unit = universe.reporter.echo(pos, msg)

def info(pos: Position, msg: String, force: Boolean): Unit = mirror.reporter.info(pos, msg, force)
def info(pos: Position, msg: String, force: Boolean): Unit = universe.reporter.info(pos, msg, force)

def hasWarnings: Boolean = mirror.reporter.hasErrors
def hasWarnings: Boolean = universe.reporter.hasErrors

def hasErrors: Boolean = mirror.reporter.hasErrors
def hasErrors: Boolean = universe.reporter.hasErrors

def warning(pos: Position, msg: String): Unit = callsiteTyper.context.warning(pos, msg)

@@ -39,7 +40,7 @@ trait FrontEnds extends scala.tools.reflect.FrontEnds {
throw new AbortMacroException(pos, msg)
}

def interactive(): Unit = mirror.reporter match {
def interactive(): Unit = universe.reporter match {
case reporter: tools.nsc.reporters.AbstractReporter => reporter.displayPrompt()
case _ => ()
}
@@ -1,34 +1,52 @@
package scala.reflect.makro
package runtime

import scala.tools.nsc.util.ScalaClassLoader

trait Infrastructure {
self: Context =>

val forJVM: Boolean = mirror.forJVM
val forJVM: Boolean = universe.forJVM

val forMSIL: Boolean = universe.forMSIL

val forInteractive: Boolean = universe.forInteractive

val forMSIL: Boolean = mirror.forMSIL
val forScaladoc: Boolean = universe.forScaladoc

val forInteractive: Boolean = mirror.forInteractive
val currentRun: Run = universe.currentRun

val forScaladoc: Boolean = mirror.forScaladoc
val libraryClassPath: List[java.net.URL] = universe.classPath.asURLs

val currentRun: Run = mirror.currentRun
lazy val libraryClassLoader: ClassLoader = {
val classpath = libraryClassPath
var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)

// [Eugene] a heuristic to detect REPL
if (universe.settings.exposeEmptyPackage.value) {
import scala.tools.nsc.interpreter._
val virtualDirectory = universe.settings.outputDirs.getSingleOutput.get
loader = new AbstractFileClassLoader(virtualDirectory, loader) {}
}

loader
}

type Run = mirror.Run
type Run = universe.Run

object Run extends RunExtractor {
def unapply(run: Run): Option[(CompilationUnit, List[CompilationUnit])] = Some(run.currentUnit, run.units.toList)
}

type CompilationUnit = mirror.CompilationUnit
type CompilationUnit = universe.CompilationUnit

object CompilationUnit extends CompilationUnitExtractor {
def unapply(compilationUnit: CompilationUnit): Option[(java.io.File, Array[Char], Tree)] = Some(compilationUnit.source.file.file, compilationUnit.source.content, compilationUnit.body)
}

val currentMacro: Symbol = expandee.symbol

val globalCache: collection.mutable.Map[Any, Any] = mirror.analyzer.globalMacroCache
val globalCache: collection.mutable.Map[Any, Any] = universe.analyzer.globalMacroCache

val cache: collection.mutable.Map[Any, Any] = mirror.analyzer.perRunMacroCache.getOrElseUpdate(currentMacro, collection.mutable.Map[Any, Any]())
val cache: collection.mutable.Map[Any, Any] = universe.analyzer.perRunMacroCache.getOrElseUpdate(currentMacro, collection.mutable.Map[Any, Any]())
}
@@ -0,0 +1,40 @@
package scala.reflect.makro
package runtime

import scala.tools.nsc.util.ScalaClassLoader

trait Mirrors {
self: Context =>

import universe._
import definitions._

class ContextMirror extends RootsBase(NoSymbol) {
val universe: self.universe.type = self.universe
def rootLoader: LazyType = rootMirror.rootLoader

val RootPackage = rootMirror.RootPackage
val RootClass = rootMirror.RootClass
val EmptyPackage = rootMirror.EmptyPackage
val EmptyPackageClass = rootMirror.EmptyPackageClass

// [Eugene++] this still doesn't solve the problem of invoking `c.typeCheck` on the code that refers to packageless symbols
override protected def mirrorMissingHook(owner: Symbol, name: Name): Symbol = {
if (owner.isRoot && isJavaClass(name.toString)) EmptyPackageClass.info decl name
else NoSymbol
}

private def isJavaClass(path: String): Boolean =
try {
val classpath = platform.classPath.asURLs

This comment has been minimized.

Copy link
@harrah

harrah Jun 9, 2012

Contributor

Why platform.classPath and not just universe.classPath? platform.classPath is never called in the compiler outside of Platform and subclasses and I think it would be best to make Global.classPath the canonical member to read.

var classLoader = ScalaClassLoader.fromURLs(classpath)
Class.forName(path, true, classLoader)

This comment has been minimized.

Copy link
@harrah

harrah Jun 9, 2012

Contributor

This creates a new class loader with the bootstrap loader as a parent and so each time isJavaClass is called it means classes in classpath need to be reloaded. If mirrorMissingHook is called more than once during a compilation, this is inefficient in memory and time.

true
} catch {
case (_: ClassNotFoundException) | (_: NoClassDefFoundError) | (_: IncompatibleClassChangeError) =>

This comment has been minimized.

Copy link
@harrah

harrah Jun 9, 2012

Contributor

This looks odd. Why catch LinkageErrors and why only a subset of LinkageErrors?

false
}

override def toString = "macro context mirror"
}
}
@@ -14,7 +14,7 @@ trait Names {
freshNameCreator.newName(name)
}

def fresh(name: Name): Name = {
name.mapName(freshNameCreator.newName(_))
def fresh[NameType <: Name](name: NameType): NameType = {
name.mapName(freshNameCreator.newName(_)).asInstanceOf[NameType]
}
}
@@ -0,0 +1,25 @@
package scala.reflect.makro
package runtime

import language.existentials
import scala.tools.reflect.ToolBox
import scala.tools.reflect.ToolBoxError

trait Parsers {
self: Context =>

def parse(code: String): Tree =
// todo. provide decent implementation
try {
import scala.reflect.runtime.{universe => ru}
val parsed = ru.rootMirror.mkToolBox().parseExpr(code)
val importer = universe.mkImporter(ru)
importer.importTree(parsed)
} catch {
case ToolBoxError(msg, cause) =>
throw new ParseError(universe.NoPosition, msg)
}

case class ParseError(val pos: Position, val msg: String) extends Throwable(msg)
object ParseError extends ParseErrorExtractor
}
@@ -9,19 +9,19 @@ package runtime
trait Reifiers {
self: Context =>

import mirror._
import universe._
import definitions._

lazy val reflectMirrorPrefix: Tree = ???

def reifyTree(prefix: Tree, tree: Tree): Tree =
scala.reflect.reify.`package`.reifyTree(mirror)(callsiteTyper, prefix, tree)
scala.reflect.reify.`package`.reifyTree(universe)(callsiteTyper, prefix, tree)

def reifyType(prefix: Tree, tpe: Type, dontSpliceAtTopLevel: Boolean = false, concrete: Boolean = false): Tree =
scala.reflect.reify.`package`.reifyType(mirror)(callsiteTyper, prefix, tpe, dontSpliceAtTopLevel, concrete)
scala.reflect.reify.`package`.reifyType(universe)(callsiteTyper, prefix, tpe, dontSpliceAtTopLevel, concrete)

def reifyRuntimeClass(tpe: Type, concrete: Boolean = true): Tree =
scala.reflect.reify.`package`.reifyRuntimeClass(mirror)(callsiteTyper, tpe, concrete)
scala.reflect.reify.`package`.reifyRuntimeClass(universe)(callsiteTyper, tpe, concrete)

def unreifyTree(tree: Tree): Tree =
Select(tree, definitions.ExprSplice)
@@ -5,12 +5,12 @@ trait Settings {
self: Context =>

def settings: List[String] = {
val optionName = mirror.settings.XmacroSettings.name
val optionName = universe.settings.XmacroSettings.name
val settings = compilerSettings.find(opt => opt.startsWith(optionName)).map(opt => opt.substring(optionName.length + 1)).getOrElse("")
settings.split(",").toList
}

def compilerSettings: List[String] = mirror.settings.recreateArgs
def compilerSettings: List[String] = universe.settings.recreateArgs

def setCompilerSettings(options: String): this.type =
// todo. is not going to work with quoted arguments with embedded whitespaces
@@ -19,7 +19,7 @@ trait Settings {
def setCompilerSettings(options: List[String]): this.type = {
val settings = new tools.nsc.Settings(_ => ())
// [Eugene] what settings should we exclude?
settings.copyInto(mirror.settings)
settings.copyInto(universe.settings)
this
}

@@ -4,5 +4,5 @@ package runtime
trait Traces extends util.Traces {
self: Context =>

def globalSettings = mirror.settings
def globalSettings = universe.settings
}
@@ -4,6 +4,6 @@ package runtime
trait TypeTags {
self: Context =>

def TypeTag[T](tpe: Type): TypeTag[T] = mirror.TypeTag[T](mirror.rootMirror, mirror.FixedMirrorTypeCreator(mirror.rootMirror, tpe))
def ConcreteTypeTag[T](tpe: Type): ConcreteTypeTag[T] = mirror.ConcreteTypeTag[T](mirror.rootMirror, mirror.FixedMirrorTypeCreator(mirror.rootMirror, tpe))
def TypeTag[T](tpe: Type): TypeTag[T] = universe.TypeTag[T](mirror, universe.FixedMirrorTypeCreator(mirror, tpe))
def ConcreteTypeTag[T](tpe: Type): ConcreteTypeTag[T] = universe.ConcreteTypeTag[T](mirror, universe.FixedMirrorTypeCreator(mirror, tpe))
}

0 comments on commit ce67870

Please sign in to comment.
You can’t perform that action at this time.