在Scalac程序的決議器階段之后,下面的case類
case class ExampleCaseClass(var s:String, var i:Int) extends ContextuallyMutable
采用中間形式:
Clazz(case class ExampleCaseClass extends ContextuallyMutable with scala.Product with scala.Serializable {
<caseaccessor> <paramaccessor> var s: String = _;
<caseaccessor> <paramaccessor> var i: Int = _;
def <init>(s: String, i: Int) = {
super.<init>();
()
}
})
但是,運行時反射呼叫:
ExampleCaseClass("Can a Scala compiler plugin transform the autogenerated accessor methods of scala case classes?", 42).getClass.getMethods.foreach(println(_))
揭示了更多的公共方法:
public boolean ExampleCaseClass.equals(java.lang.Object)
public java.lang.String ExampleCaseClass.toString()
public int ExampleCaseClass.hashCode()
public static ExampleCaseClass ExampleCaseClass.apply(java.lang.String,int)
public int ExampleCaseClass.i()
public java.lang.String ExampleCaseClass.s()
public ExampleCaseClass ExampleCaseClass.copy(java.lang.String,int)
public void ExampleCaseClass.i_$eq(int)
public scala.collection.Iterator ExampleCaseClass.productElementNames()
public java.lang.String ExampleCaseClass.productElementName(int)
public void ExampleCaseClass.s_$eq(java.lang.String)
public int ExampleCaseClass.copy$default$2()
public boolean ExampleCaseClass.canEqual(java.lang.Object)
public java.lang.String ExampleCaseClass.productPrefix()
public int ExampleCaseClass.productArity()
public java.lang.Object ExampleCaseClass.productElement(int)
public scala.collection.Iterator ExampleCaseClass.productIterator()
public java.lang.String ExampleCaseClass.copy$default$1()
public static scala.Function1 ExampleCaseClass.tupled()
public static scala.Option ExampleCaseClass.unapply(ExampleCaseClass)
public static scala.Function1 ExampleCaseClass.curried()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
很明顯,一些后續的編譯器階段會創建屬性訪問器方法:
public int ExampleCaseClass.i()
public java.lang.String ExampleCaseClass.s()
public void ExampleCaseClass.i_$eq(int)
public void ExampleCaseClass.s_$eq(java.lang.String)
哪個編譯階段會生成這些訪問器方法以及哪種編譯器插件(或其他方式)可能會阻止或轉換它們?
詢問者已經進行了大量實驗,以消除或重塑:
<caseaccessor> <paramaccessor> var s: String = _;
<caseaccessor> <paramaccessor> var i: Int = _;
portions of the case class, and also with injecting the desired accessor methods in advance but no combination has met the desired outcome. They either fail to compile because of naming conflicts that arise in subsequent compilation phases, or they alter the parameter names in constructor, apply, and accessor methods.
Can a scala compiler plugin transform synthetic accessors at all? Does the Java Compiler introduce these methods? If so, should the enquirer look to Javac plugins and what analogues might serve the Scala.js and Scala native compilation targets?
Thank you for any consideration.
uj5u.com熱心網友回復:
case class擴展發生在不止一個地方,請參閱另一個問題。
與其撰寫一個新插件來禁止使用var它,不如向 Wartremover 或 ScalaFix 添加新規則。事實上,這些規則已經存在:
- 禁止
var使用 Wartremover - 將其與致命警告相結合以導致編譯失敗var - 禁止
var使用 ScalaFix
如果您想添加更詳細的規則……撰寫您自己的 Wartremover/ScalaFix 規則仍然會更容易(后者可能是首選,因為 Scala 3 已經支持它)。
如果你真的需要一個自定義的編譯器插件來處理編譯器生成的代碼......看看Better-toString plugin。它逐個階段添加自己的"parser"階段。但我不希望洗掉自動生成的實作。充其量您可以在規范允許的情況下手動覆寫它們。
uj5u.com熱心網友回復:
詢問者從示例中找到了具有持久性和幫助的 Scala 3 解決方案:
- Better -tostring一個插件,它演示了 Polyvariant 的條件方法插入。
- Scala 中心開發者:Fengyun Liu 的Scala 3示例和教程中的編譯器插件開發。該視頻提供了對編譯器階段的見解,并且該示例闡明了方法體生成語法。特別是,可用的檔案并不容易闡明如何
println從由編譯器插件生成為樹的方法體進行呼叫,但 Liu 的示例插件演示了requireModule和requiredMethod. - Scala 3 Compiler Plugin Documentation為如何開始撰寫插件提供了一個非常好的模板。最終的解決方案看起來非常相似。
截至 2021 年 11 月 26 日,Scala 2.13 尚無解決方案,但這也許只是意味著是時候升級了。
final class BlockMutatorPlugin extends StandardPlugin {
override val name: String = "BlockMutatorPlugin"
override val description: String = "Scala Compiler Plugin for blocking ContextuallyMutable setter methods."
override def init(options: List[String]): List[PluginPhase] = List(new BlockContextuallyMutableSetters)
}
class BlockContextuallyMutableSetters extends PluginPhase {
val phaseName = "blockGetter"
/* Running this plugin after phases before ElimErasedValueType
resulted in the replacement of the generated setter methods by the synthetic
default versions. By the time that this ElimErasedValueType phase ends, the
defaults already existed, so this plugin could augment them safely. */
override val runsAfter = Set(ElimErasedValueType.name)
private var printBlocked: Tree = _
override def prepareForTemplate(tree: tpd.Template)(using ctx: Context): Context = {
val cnsl = requiredModule("scala.Predef")
val prntln: PreName = "println".toTermName
val say = cnsl.requiredMethod(prntln, List[Types.Type](ctx.definitions.ObjectType))
printBlocked = ref(say).appliedTo(Literal(Constant("Blocked!")))
ctx
}
override def transformTemplate(tree: Template)(using ctx: Context): Tree = {
if (tree.parents.filter(_.symbol.name.toString.equals("ContextuallyMutable")).nonEmpty) {
cpy.Template(tree)(
body = tree.body.collect {
case dd: DefDef if dd.name.isSetterName => DefDef(
dd.symbol.asInstanceOf[Symbols.TermSymbol],
printBlocked
)
case x => x
}
).asInstanceOf[Tree]
} else tree
}
}
這項作業中最重要的部分涉及發現這個插件應該遵循哪個編譯器階段。到目前為止,Scala 2.13.6 中的類似努力都失敗了;這個原始堆疊溢位問題所尋求的 Scala 2 解決方案的唯一剩余障礙。因此,除非將來的編輯使用 Scala 2,否則詢問者不會將他自己的答案標記為已接受的解決方案。在那之前,您的回復可能會聲稱該名稱。
對于任何傾向于嘗試編譯此示例的人,上面的代碼需要以下匯入陳述句:
import dotty.tools.dotc.ast.tpd
import tpd.*
import dotty.tools.dotc.core.*
import Names.PreName
import Symbols.{ClassSymbol, requiredMethod, requiredModule}
import Decorators.*
import NameOps.*
import Contexts.Context
import Constants.Constant
import dotty.tools.dotc.plugins.*
import dotty.tools.dotc.transform.ElimErasedValueType
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/368362.html
標籤:scala scala-macros scalac scala-compiler javacompiler
上一篇:AkkaStreams:如何使用Flow為圖形形成入口和出口
下一篇:在專案中更改vue版本
