Skip to content

Use indentation syntax in Staging/Inspector internals #8536

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions staging/src/scala/quoted/staging/QuoteCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import scala.quoted.{Expr, QuoteContext, Type}
/** Compiler that takes the contents of a quoted expression `expr` and produces
* a class file with `class ' { def apply: Object = expr }`.
*/
private class QuoteCompiler extends Compiler {
private class QuoteCompiler extends Compiler:

/** Either `Left` with name of the classfile generated or `Right` with the value contained in the expression */
private[this] var result: Either[String, Any] = null
Expand All @@ -40,15 +40,14 @@ private class QuoteCompiler extends Compiler {
override protected def picklerPhases: List[List[Phase]] =
List(List(new ReifyQuotes))

override def newRun(implicit ctx: Context): ExprRun = {
override def newRun(implicit ctx: Context): ExprRun =
reset()
new ExprRun(this, ctx.addMode(Mode.ReadPositions))
}

def outputClassName: TypeName = "Generated$Code$From$Quoted".toTypeName

/** Frontend that receives a scala.quoted.Expr or scala.quoted.Type as input */
class QuotedFrontend extends Phase {
class QuotedFrontend extends Phase:
import tpd._

def phaseName: String = "quotedFrontend"
Expand All @@ -68,14 +67,14 @@ private class QuoteCompiler extends Compiler {
cls.enter(unitCtx.newDefaultConstructor(cls), EmptyScope)
val meth = unitCtx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered

val quoted = {
val quoted =
given Context = unitCtx.withOwner(meth)
val qctx = dotty.tools.dotc.quoted.QuoteContext()
val quoted = PickledQuotes.quotedExprToTree(exprUnit.exprBuilder.apply(qctx))
checkEscapedVariables(quoted, meth)
}
end quoted

getLiteral(quoted) match {
getLiteral(quoted) match
case Some(value) =>
result = Right(value)
None // Stop copilation here we already have the result
Expand All @@ -86,28 +85,29 @@ private class QuoteCompiler extends Compiler {
val source = SourceFile.virtual("<quoted.Expr>", "")
result = Left(outputClassName.toString)
Some(CompilationUnit(source, tree, forceTrees = true))
}
}

/** Get the literal value if this tree only contains a literal tree */
@tailrec private def getLiteral(tree: Tree): Option[Any] = tree match {
case Literal(lit) => Some(lit.value)
case Block(Nil, expr) => getLiteral(expr)
case Inlined(_, Nil, expr) => getLiteral(expr)
case _ => None
}
@tailrec private def getLiteral(tree: Tree): Option[Any] =
tree match
case Literal(lit) => Some(lit.value)
case Block(Nil, expr) => getLiteral(expr)
case Inlined(_, Nil, expr) => getLiteral(expr)
case _ => None

def run(implicit ctx: Context): Unit = unsupported("run")
}

class ExprRun(comp: QuoteCompiler, ictx: Context) extends Run(comp, ictx) {
end QuotedFrontend

class ExprRun(comp: QuoteCompiler, ictx: Context) extends Run(comp, ictx):
/** Unpickle and optionally compile the expression.
* Returns either `Left` with name of the classfile generated or `Right` with the value contained in the expression.
*/
def compileExpr(exprBuilder: QuoteContext => Expr[_]): Either[String, Any] = {
def compileExpr(exprBuilder: QuoteContext => Expr[_]): Either[String, Any] =
val units = new ExprCompilationUnit(exprBuilder) :: Nil
compileUnits(units)
result
}
}
}

end ExprRun

end QuoteCompiler
37 changes: 18 additions & 19 deletions staging/src/scala/quoted/staging/QuoteDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,26 @@ import scala.annotation.tailrec
*
* @param appClassloader classloader of the application that generated the quotes
*/
private class QuoteDriver(appClassloader: ClassLoader) extends Driver {
private class QuoteDriver(appClassloader: ClassLoader) extends Driver:
import tpd._

private[this] val contextBase: ContextBase = new ContextBase

def run[T](exprBuilder: QuoteContext => Expr[T], settings: Toolbox.Settings): T = {
val outDir: AbstractFile = settings.outDir match {
case Some(out) =>
val dir = Directory(out)
dir.createDirectory()
new PlainDirectory(Directory(out))
case None =>
new VirtualDirectory("<quote compilation output>")
}
def run[T](exprBuilder: QuoteContext => Expr[T], settings: Toolbox.Settings): T =
val outDir: AbstractFile =
settings.outDir match
case Some(out) =>
val dir = Directory(out)
dir.createDirectory()
new PlainDirectory(Directory(out))
case None =>
new VirtualDirectory("<quote compilation output>")
end outDir

val (_, ctx0: Context) = setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)
val ctx = setToolboxSettings(ctx0.fresh.setSetting(ctx0.settings.outputDir, outDir), settings)

new QuoteCompiler().newRun(ctx).compileExpr(exprBuilder) match {
new QuoteCompiler().newRun(ctx).compileExpr(exprBuilder) match
case Right(value) =>
value.asInstanceOf[T]

Expand All @@ -50,21 +51,19 @@ private class QuoteDriver(appClassloader: ClassLoader) extends Driver {
val inst = clazz.getConstructor().newInstance()

method.invoke(inst).asInstanceOf[T]
}
}
end match

override def initCtx: Context = {
end run

override def initCtx: Context =
val ictx = contextBase.initialCtx
ictx.settings.classpath.update(ClasspathFromClassloader(appClassloader))(ictx)
ictx
}

private def setToolboxSettings(ctx: FreshContext, settings: Toolbox.Settings): ctx.type = {
private def setToolboxSettings(ctx: FreshContext, settings: Toolbox.Settings): ctx.type =
ctx.setSetting(ctx.settings.YshowRawQuoteTrees, settings.showRawTree)
// An error in the generated code is a bug in the compiler
// Setting the throwing reporter however will report any exception
ctx.setReporter(new ThrowingReporter(ctx.reporter))
}

}

end QuoteDriver
40 changes: 21 additions & 19 deletions staging/src/scala/quoted/staging/Toolbox.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ package staging
import scala.annotation.implicitNotFound

@implicitNotFound("Could not find implicit scala.quoted.staging.Toolbox.\n\nDefault toolbox can be instantiated with:\n `given scala.quoted.staging.Toolbox = scala.quoted.staging.Toolbox.make(getClass.getClassLoader)`\n\n")
trait Toolbox {
trait Toolbox:
def run[T](expr: QuoteContext => Expr[T]): T
}

object Toolbox {
object Toolbox:

/** Create a new instance of the toolbox using the the classloader of the application.
*
Expand All @@ -22,28 +21,30 @@ object Toolbox {
* @param settings toolbox settings
* @return A new instance of the toolbox
*/
def make(appClassloader: ClassLoader)(implicit settings: Settings): Toolbox = new Toolbox {
def make(appClassloader: ClassLoader)(implicit settings: Settings): Toolbox =
new Toolbox:

private[this] val driver: QuoteDriver = new QuoteDriver(appClassloader)
private[this] val driver: QuoteDriver = new QuoteDriver(appClassloader)

private[this] var running = false
private[this] var running = false

def run[T](exprBuilder: QuoteContext => Expr[T]): T = synchronized {
try {
if (running) // detected nested run
throw new ScopeException("Cannot call `scala.quoted.staging.run(...)` within a another `run(...)`")
running = true
driver.run(exprBuilder, settings)
} finally {
running = false
def run[T](exprBuilder: QuoteContext => Expr[T]): T = synchronized {
try
if (running) // detected nested run
throw new ScopeException("Cannot call `scala.quoted.staging.run(...)` within a another `run(...)`")
running = true
driver.run(exprBuilder, settings)
finally
running = false
end try
}
}
}

end new

/** Setting of the Toolbox instance. */
case class Settings private (outDir: Option[String], showRawTree: Boolean, compilerArgs: List[String])

object Settings {
object Settings:

implicit def default: Settings = make()

Expand All @@ -58,6 +59,7 @@ object Toolbox {
compilerArgs: List[String] = Nil
): Settings =
new Settings(outDir, showRawTree, compilerArgs)
}

}
end Settings

end Toolbox
8 changes: 4 additions & 4 deletions staging/src/scala/quoted/staging/staging.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package scala.quoted

package object staging {
package object staging:

/** Evaluate the contents of this expression and return the result.
* It provides a new QuoteContext that is only valid within the scope the argument.
Expand Down Expand Up @@ -32,7 +32,7 @@ package object staging {
* This method should not be called in a context where there is already has a `QuoteContext`
* such as within a `run` or a `withQuoteContext`.
*/
def withQuoteContext[T](thunk: QuoteContext ?=> T)(using toolbox: Toolbox): T = {
def withQuoteContext[T](thunk: QuoteContext ?=> T)(using toolbox: Toolbox): T =
val noResult = new Object
var result: T = noResult.asInstanceOf[T]
def dummyRun(using QuoteContext): Expr[Unit] = {
Expand All @@ -42,6 +42,6 @@ package object staging {
toolbox.run(dummyRun(using _))
assert(result != noResult) // toolbox.run should have thrown an exception
result
}
end withQuoteContext

}
end staging
32 changes: 18 additions & 14 deletions tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import dotty.tools.dotc.util.ClasspathFromClassloader

import java.io.File.pathSeparator

trait TastyInspector { self =>
trait TastyInspector:
self =>

/** Process a TASTy file using TASTy reflect */
protected def processCompilationUnit(reflect: Reflection)(root: reflect.Tree): Unit
Expand All @@ -24,44 +25,47 @@ trait TastyInspector { self =>
* @param classpath Classpath where the classes are located
* @param classes classes to be inspected
*/
def inspect(classpath: String, classes: List[String]): Unit = {
def inspect(classpath: String, classes: List[String]): Unit =
if (classes.isEmpty)
throw new IllegalArgumentException("Parameter classes should no be empty")

class InspectorDriver extends Driver {
class InspectorDriver extends Driver:
override protected def newCompiler(implicit ctx: Context): Compiler = new TastyFromClass
}

class TastyFromClass extends TASTYCompiler {
class TastyFromClass extends TASTYCompiler:

override protected def frontendPhases: List[List[Phase]] =
List(new ReadTasty) :: // Load classes from tasty
Nil

override protected def picklerPhases: List[List[Phase]] = Nil

override protected def transformPhases: List[List[Phase]] = Nil

override protected def backendPhases: List[List[Phase]] =
List(new TastyInspectorPhase) :: // Print all loaded classes
Nil

override def newRun(implicit ctx: Context): Run = {
override def newRun(implicit ctx: Context): Run =
reset()
new TASTYRun(this, ctx.fresh.addMode(Mode.ReadPositions).addMode(Mode.ReadComments))
}
}

class TastyInspectorPhase extends Phase {
end TastyFromClass

class TastyInspectorPhase extends Phase:

override def phaseName: String = "tastyInspector"

override def run(implicit ctx: Context): Unit = {
override def run(implicit ctx: Context): Unit =
val reflect = ReflectionImpl(ctx)
self.processCompilationUnit(reflect)(ctx.compilationUnit.tpdTree.asInstanceOf[reflect.Tree])
}
}

end TastyInspectorPhase

val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader)
val args = "-from-tasty" :: "-Yretain-trees" :: "-classpath" :: s"$classpath$pathSeparator$currentClasspath" :: classes
(new InspectorDriver).process(args.toArray)
}
end inspect


}
end TastyInspector