Scala 3のcompiler plugin、同じsbt project内部で開発と実行しようとしても、単純にやるとClassLoaderがキャッシュしてしまって、pluginのソースいじっても反映されなくて、sbt丸ごと立ち上げ直さないといけない・・・?https://t.co/5Md7rnUReX— Kenji Yoshida (@xuwei_k) January 28, 2022
Scala 3のcompiler plugin、同じsbt project内部で開発と実行しようとしても、単純にやるとClassLoaderがキャッシュしてしまって、pluginのソースいじっても反映されなくて、sbt丸ごと立ち上げ直さないといけない・・・?https://t.co/5Md7rnUReX
case class A extends AnyRef with Product with Serializable { private[this] val x: Int = _; def x: Int = A.this.x; def (x: Int): this.A = { A.super.(); () }; def copy(x: Int = x): this.A = new A(x); def copy$default$1: Int = A.this.x; override def productPrefix: String = "A"; def productArity: Int = 1; def productElement(x$1: Int): Any = x$1 match { case 0 => A.this.x case _ => scala.runtime.Statics.ioobe[Any](x$1) }; override def productIterator: Iterator[Any] = scala.runtime.ScalaRunTime.typedProductIterator[Any](A.this); def canEqual(x$1: Any): Boolean = x$1.$isInstanceOf[this.A](); override def productElementName(x$1: Int): String = x$1 match { case 0 => "x" case _ => scala.runtime.Statics.ioobe[String](x$1) }; override def hashCode(): Int = { var acc: Int = -889275714; acc = scala.runtime.Statics.mix(acc, A.this.productPrefix.hashCode()); acc = scala.runtime.Statics.mix(acc, x); scala.runtime.Statics.finalizeHash(acc, 1) }; override def toString(): String = scala.runtime.ScalaRunTime._toString(A.this); override def equals(x$1: Any): Boolean = A.this.eq(x$1.asInstanceOf[Object]).||(x$1 match { case (_: this.A) => true case _ => false }.&&({ val A$1: this.A = x$1.asInstanceOf[this.A]; A.this.x.==(A$1.x).&&(A$1.canEqual(A.this)) })) }; private object A extends scala.runtime.AbstractFunction1[Int,this.A] with java.io.Serializable { def (): this.A.type = { A.super.(); () }; final override def toString(): String = "A"; case def apply(x: Int): this.A = new A(x); case def unapply(x$0: this.A): Option[Int] = if (x$0.eq(null)) scala.None else Some.apply[Int](x$0.x) }
$ scala -Xshow-phases parser typer inlinedPositions sbt-deps extractSemanticDB posttyper prepjsinterop sbt-api SetRootTree pickler inlining postInlining staging pickleQuotes {firstTransform, checkReentrant, elimPackagePrefixes, cookComments, checkStatic, checkLoopingImplicits, betaReduce, inlineVals, expandSAMs} initChecker {elimRepeated, protectedAccessors, extmethods, uncacheGivenAliases, byNameClosures, hoistSuperArgs, specializeApplyMethods, refchecks, tryCatchPatterns, patternMatcher} {elimOpaque, explicitJSClasses, explicitOuter, explicitSelf, elimByName, stringInterpolatorOpt} {pruneErasedDefs, uninitializedDefs, inlinePatterns, vcInlineMethods, seqLiterals, intercepted, getters, specializeFunctions, liftTry, collectNullableFields, elimOuterSelect, resolveSuper, functionXXLForwarders, paramForwarding, genericTuples, letOverApply, arrayConstructors} erasure {elimErasedValueType, pureStats, vcElideAllocations, arrayApply, addLocalJSFakeNews, elimPolyFunction, tailrec, completeJavaEnums, mixin, lazyVals, memoize, nonLocalReturns, capturedVars} {constructors, instrumentation} {lambdaLift, elimStaticThis, countOuterAccesses} {dropOuterAccessors, checkNoSuperThis, flatten, transformWildcards, moveStatic, expandPrivate, restoreScopes, selectStatic, junitBootstrappers, Collect entry points, collectSuperCalls, repeatableAnnotations} genSJSIR genBCode
$ scala -Xshow-phases phase name id description ---------- -- ----------- parser 1 parse source into ASTs, perform simple desugaring namer 2 resolve names, attach symbols to named trees packageobjects 3 load package objects typer 4 the meat and potatoes: type the trees superaccessors 5 add super accessors in traits and nested classes extmethods 6 add extension methods for inline classes pickler 7 serialize symbol tables refchecks 8 reference/override checking, translate nested objects patmat 9 translate match expressions uncurry 10 uncurry, translate function values to anonymous classes fields 11 synthesize accessors and fields, add bitmaps for lazy vals tailcalls 12 replace tail calls by jumps specialize 13 @specialized-driven class and method specialization explicitouter 14 this refs to outer pointers erasure 15 erase types, add interfaces for traits posterasure 16 clean up erased inline classes lambdalift 17 move nested functions to top level constructors 18 move field definitions into constructors flatten 19 eliminate inner classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls delambdafy 22 remove lambdas jvm 23 generate JVM bytecode terminal 24 the last phase during a compilation run
import dotty.tools.dotc.plugins.PluginPhase import dotty.tools.dotc.plugins.StandardPlugin class WartremoverPlugin extends StandardPlugin { override def name = "wartremover" override def description = "wartremover" override def init(options: List[String]): List[PluginPhase] = { // ここで引数parseする val newPhase = new WartremoverPhase( // parseした引数から生成したもの渡す ) newPhase :: Nil }
class WartremoverPhase( errorWarts: List[WartTraverser], warningWarts: List[WartTraverser] // その他の引数省略 ) extends PluginPhase { override def phaseName = "wartremover" // Typerの後に実行して!という指定 override val runsAfter = Set(TyperPhase.name) // 他にも色々あるが、おそらくこれだけで原理上全部のTree辿れる? override def prepareForUnit(tree: Tree)(using c: Context) = { // Contextと新しいQuotesを生成 val c2 = QuotesCache.init(c.fresh) val q = scala.quoted.runtime.impl.QuotesImpl()(using c2) def runWart(w: WartTraverser, onlyWarning: Boolean): Unit = { val universe = new WartUniverse( quotes = q, traverser = w, onlyWarning = onlyWarning, // その他引数 ) val traverser = w.apply(universe) // compiler内部のTreeをlibrary側のTreeに無理矢理キャスト! val t = tree.asInstanceOf[traverser.q.reflect.Tree] try { traverser.traverseTree(t)(t.symbol) } catch { case NonFatal(e) => // 場合によってログ出す } } errorWarts.foreach(w => runWart(w = w, onlyWarning = false)) warningWarts.foreach(w => runWart(w = w, onlyWarning = true)) c }