Skip to content

Commit 14098b2

Browse files
committed
support both ordering and research plugin
1 parent 817e6ff commit 14098b2

File tree

5 files changed

+99
-15
lines changed

5 files changed

+99
-15
lines changed

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class Run(comp: Compiler, ictx: Context) {
112112
if (ctx.settings.YtestPickler.value) List("pickler")
113113
else ctx.settings.YstopAfter.value
114114

115-
val pluginPlan = ctx.plugins.foldRight(ctx.phasePlan) { (plug, plan) => plug.init(plan) }
115+
val pluginPlan = ctx.addPluginPhases(ctx.phasePlan)
116116
val phases = ctx.squashPhases(pluginPlan,
117117
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, stopAfter, ctx.settings.Ycheck.value)
118118
ctx.usePhases(phases)

compiler/src/dotty/tools/dotc/plugins/Plugin.scala

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,47 @@ import java.io.InputStream
1111
import scala.collection.mutable
1212
import scala.util.{ Try, Success, Failure }
1313

14+
trait PluginPhase extends Phase {
15+
def runsBefore: Set[Class[_ <: Phase]] = Set.empty
16+
}
17+
1418
trait Plugin {
1519
/** The name of this plugin */
1620
def name: String
1721

1822
/** A one-line description of the plugin */
1923
def description: String
2024

25+
/** The phases that this plugin defines */
26+
def components: List[PluginPhase] = Nil
27+
28+
/** Is this plugin a research plugin?
29+
*
30+
* Research plugin receives a phase plan and return a new phase plan, while
31+
* non-research plugin returns a list of phases to be inserted.
32+
*/
33+
def research: Boolean = false
34+
35+
/** Handle any plugin-specific options.
36+
* The user writes `-P:plugname:opt1,opt2`,
37+
* but the plugin sees `List(opt1, opt2)`.
38+
*/
2139
def options(implicit ctx: Context): List[String] = {
2240
// Process plugin options of form plugin:option
2341
def namec = name + ":"
2442
ctx.settings.pluginOptions.value filter (_ startsWith namec) map (_ stripPrefix namec)
2543
}
2644

27-
/** Handle any plugin-specific options.
28-
* The user writes `-P:plugname:opt1,opt2`,
29-
* but the plugin sees `List(opt1, opt2)`.
30-
* The plugin can opt out of further processing
31-
* by returning false. For example, if the plugin
32-
* has an "enable" flag, now would be a good time
33-
* to sit on the bench.
34-
* @param options plugin arguments
35-
* @param error error function
36-
* @return true to continue, or false to opt out
45+
/** Non-research plugins should override this method to return the phases
46+
*
47+
* @return a list of phases to be added to the phase plan
3748
*/
49+
def init()(implicit ctx: Context): List[Phase] = Nil
50+
51+
/** Research plugins should override this method to return the new phase plan
52+
*
53+
* @return the new phase plan
54+
*/
3855
def init(phases: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] = phases
3956

4057
/** A description of this plugin's options, suitable as a response
@@ -72,11 +89,11 @@ object Plugin {
7289
else PluginDescription.fromXML(is)
7390

7491
val xmlEntry = new java.util.jar.JarEntry(PluginXML)
75-
Try(read(new Jar(jarp.jfile).getEntryStream(xmlEntry)))
92+
Try(read(new Jar(jarp.jpath.toFile).getEntryStream(xmlEntry)))
7693
}
7794

7895
private def loadDescriptionFromFile(f: Path): Try[PluginDescription] =
79-
Try(PluginDescription.fromXML(new java.io.FileInputStream(f.jfile)))
96+
Try(PluginDescription.fromXML(new java.io.FileInputStream(f.jpath.toFile)))
8097

8198
type AnyClass = Class[_]
8299

compiler/src/dotty/tools/dotc/plugins/Plugins.scala

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ package plugins
33

44
import core._
55
import Contexts._
6-
import dotty.tools.dotc.config.PathResolver
6+
import config.PathResolver
77
import dotty.tools.io._
8+
import Phases._
89

910
/** Support for run-time loading of compiler plugins.
1011
*
@@ -80,7 +81,7 @@ trait Plugins {
8081
}
8182
}
8283

83-
val plugs = pick(roughPluginsList, Set())
84+
val plugs = pick(roughPluginsList, ctx.phasePlan.flatten.map(_.phaseName).toSet)
8485

8586
// Verify required plugins are present.
8687
for (req <- ctx.settings.require.value ; if !(plugs exists (_.name == req)))
@@ -113,4 +114,65 @@ trait Plugins {
113114
(for (plug <- roughPluginsList ; help <- plug.optionsHelp) yield {
114115
"\nOptions for plugin '%s':\n%s\n".format(plug.name, help)
115116
}).mkString
117+
118+
/** Add plugin phases to phase plan */
119+
def addPluginPhases(plan: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] = {
120+
import scala.collection.mutable.{ Set => MSet, Map => MMap }
121+
type OrderingReq = (MSet[Class[_]], MSet[Class[_]])
122+
123+
val orderRequirements = MMap[Class[_], OrderingReq]()
124+
125+
def updateOrdering(phase: PluginPhase): Unit = {
126+
val runsBefore: MSet[Class[_]] = MSet(phase.runsBefore.toSeq: _*)
127+
val runsAfter: MSet[Class[_]] = MSet(phase.runsAfter.toSeq: _*)
128+
129+
if (!orderRequirements.contains(phase.getClass)) {
130+
orderRequirements.update(phase.getClass, (runsBefore, runsAfter) )
131+
}
132+
133+
runsBefore.foreach { phaseClass =>
134+
if (!orderRequirements.contains(phaseClass))
135+
orderRequirements.update(phaseClass, (MSet.empty, MSet.empty))
136+
val (_, runsAfter) = orderRequirements(phaseClass)
137+
runsAfter += phase.getClass
138+
}
139+
140+
runsAfter.foreach { phaseClass =>
141+
if (!orderRequirements.contains(phaseClass))
142+
orderRequirements.update(phaseClass, (MSet.empty, MSet.empty))
143+
val (runsBefore, _) = orderRequirements(phaseClass)
144+
runsBefore += phase.getClass
145+
}
146+
}
147+
148+
// add non-research plugins
149+
var updatedPlan = plan
150+
plugins.filter(!_.research).foreach { plug =>
151+
plug.components.foreach { phase =>
152+
updateOrdering(phase)
153+
154+
val beforePhases: MSet[Class[_]] = MSet(phase.runsBefore.toSeq: _*)
155+
val afterPhases: MSet[Class[_]] = MSet(phase.runsAfter.toSeq: _*)
156+
157+
val (before, after) = updatedPlan.span { ps =>
158+
val classes = ps.map(_.getClass)
159+
afterPhases --= classes
160+
!classes.exists(beforePhases.contains) // beforeReq satisfied
161+
}
162+
163+
// check afterReq
164+
// error can occur if: a < b, b < c, c < a
165+
after.foreach { ps =>
166+
val classes = ps.map(_.getClass)
167+
if (classes.exists(afterPhases)) // afterReq satisfied
168+
throw new Exception(s"Ordering conflict for plugin ${plug.name}")
169+
}
170+
171+
updatedPlan = before ++ (List(phase) :: after)
172+
}
173+
}
174+
175+
// add research plugins
176+
ctx.plugins.filter(_.research).foldRight(updatedPlan) { (plug, plan) => plug.init(plan) }
177+
}
116178
}

tests/plugins/neg/divideZero/plugin_1.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class DivideZero extends MiniPhase with Plugin {
1313
val name: String = "divideZero"
1414
override val description: String = "divide zero check"
1515

16+
override val research = true
17+
1618
val phaseName = name
1719

1820
override def init(phases: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] = {

tests/plugins/pos/divideZero/plugin_1.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class DivideZero extends MiniPhase with Plugin {
1515

1616
val phaseName = name
1717

18+
override val research = true
19+
1820
override def init(phases: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] = {
1921
val (before, after) = phases.span(ps => !ps.exists(_.phaseName == "pickler"))
2022
before ++ (List(this) :: after)
@@ -35,3 +37,4 @@ class DivideZero extends MiniPhase with Plugin {
3537
tree
3638
}
3739
}
40+

0 commit comments

Comments
 (0)