Skip to content

Commit 1f2759e

Browse files
committed
unit test plugin schedule algorithm
1 parent 5d56fb7 commit 1f2759e

File tree

3 files changed

+156
-8
lines changed

3 files changed

+156
-8
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ trait Plugin {
3333

3434
/** Non-research plugins should override this method to return the phases
3535
*
36-
* @param options: commandline options to the plugin, `-P:plugname:opt1,opt2`
36+
* @param options: commandline options to the plugin, `-P:plugname:opt1,opt2` becomes List(opt1, opt2)
3737
* @return a list of phases to be added to the phase plan
3838
*/
3939
def init(options: List[String]): List[PluginPhase] = ???
4040

4141
/** Research plugins should override this method to return the new phase plan
4242
*
43-
* @param options: commandline options to the plugin, `-P:plugname:opt1,opt2`
43+
* @param options: commandline options to the plugin, `-P:plugname:opt1,opt2` becomes List(opt1, opt2)
4444
* @param plan: the given phase plan
4545
* @return the new phase plan
4646
*/

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,22 @@ object Plugins {
145145
type OrderingReq = (MSet[Class[_]], MSet[Class[_]])
146146

147147
val orderRequirements = MMap[Class[_], OrderingReq]()
148+
val existingPhases = {
149+
val set = MSet.empty[Class[_]]
150+
for (ps <- plan; p <- ps) set += p.getClass
151+
set
152+
}
148153

149154
def updateOrdering(phase: PluginPhase): Unit = {
150155
val runsBefore: MSet[Class[_]] = MSet(phase.runsBefore.toSeq: _*)
151156
val runsAfter: MSet[Class[_]] = MSet(phase.runsAfter.toSeq: _*)
152157

153158
if (!orderRequirements.contains(phase.getClass)) {
154159
orderRequirements.update(phase.getClass, (runsBefore, runsAfter) )
160+
} else {
161+
val (runsBefore1, runsAfter1) = orderRequirements(phase.getClass)
162+
runsAfter1 ++= runsAfter
163+
runsBefore1 ++= runsBefore
155164
}
156165

157166
runsBefore.foreach { phaseClass =>
@@ -174,28 +183,32 @@ object Plugins {
174183
plug.init(optionFn(plug)).foreach { phase =>
175184
updateOrdering(phase)
176185

177-
val beforePhases: MSet[Class[_]] = MSet(phase.runsBefore.toSeq: _*)
178-
val afterPhases: MSet[Class[_]] = MSet(phase.runsAfter.toSeq: _*)
186+
val (runsBefore1, runsAfter1) = orderRequirements(phase.getClass)
187+
val runsBefore: MSet[Class[_]] = runsBefore1 & existingPhases // MSet(runsBefore1.filter(existingPhases.contains).toSeq: _*)
188+
val runsAfter: MSet[Class[_]] = runsAfter1 & existingPhases // MSet(runsAfter1.filter(existingPhases.contains).toSeq: _*)
179189

180190
// beforeReq met after the split
181191
val (before, after) = updatedPlan.span { ps =>
182192
val classes = ps.map(_.getClass)
183-
afterPhases --= classes
193+
val runsAfterSat = runsAfter.isEmpty
194+
runsAfter --= classes
184195
// Prefer the point immediately before the first beforePhases.
185196
// If beforePhases not specified, insert at the point immediately
186197
// after the last afterPhases.
187-
!classes.exists(beforePhases.contains) &&
188-
!(beforePhases.isEmpty && afterPhases.isEmpty)
198+
!classes.exists(runsBefore.contains) &&
199+
!(runsBefore.isEmpty && runsAfterSat)
189200
}
190201

191202
// check afterReq
192203
// error can occur if: a < b, b < c, c < a
193204
after.foreach { ps =>
194205
val classes = ps.map(_.getClass)
195-
if (classes.exists(afterPhases)) // afterReq satisfied
206+
if (classes.exists(runsAfter)) // afterReq satisfied
196207
throw new Exception(s"Ordering conflict for plugin ${plug.name}")
197208
}
198209

210+
211+
existingPhases += phase.getClass
199212
updatedPlan = before ++ (List(phase) :: after)
200213
}
201214
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package dotty.tools.dotc.plugins
2+
3+
import org.junit.Test
4+
5+
import dotty.tools.dotc._
6+
import plugins._
7+
import transform.MegaPhase.MiniPhase
8+
9+
class PluginsTest {
10+
class TestPhase extends PluginPhase { def phaseName = this.getClass.getName }
11+
class P1 extends TestPhase
12+
class P2 extends TestPhase
13+
class P3a extends TestPhase
14+
class P3b extends TestPhase
15+
class P3c extends TestPhase
16+
class P3d extends TestPhase
17+
class P3e extends TestPhase
18+
class P4 extends TestPhase
19+
class P5 extends TestPhase
20+
class P6a extends TestPhase
21+
class P6b extends TestPhase
22+
class P6c extends TestPhase
23+
class P6d extends TestPhase
24+
class P6e extends TestPhase
25+
class P7 extends TestPhase
26+
class P8 extends TestPhase
27+
28+
class TestPlugin extends TestPhase with Plugin {
29+
def name = this.getClass.getName
30+
override def description = ""
31+
32+
override def init(options: List[String]): List[PluginPhase] = this :: Nil
33+
}
34+
35+
val basicPlan = List(
36+
List(new P1),
37+
List(new P2),
38+
List(new P3a, new P3b, new P3c, new P3d, new P3e),
39+
List(new P4),
40+
List(new P5),
41+
List(new P6a, new P6b, new P6c, new P6d, new P6e),
42+
List(new P7),
43+
List(new P8)
44+
)
45+
46+
@Test
47+
def insertAfter = {
48+
object M1 extends TestPlugin {
49+
override val runsAfter = Set(classOf[P3d])
50+
}
51+
52+
val updatedPlan = Plugins.schedule(basicPlan, M1 :: Nil, _ => Nil)
53+
assert(updatedPlan(3)(0) eq M1)
54+
}
55+
56+
@Test
57+
def insertBefore = {
58+
object ConstFold extends TestPlugin {
59+
override val runsBefore = Set(classOf[P7])
60+
}
61+
62+
val updatedPlan = Plugins.schedule(basicPlan, ConstFold :: Nil, _ => Nil)
63+
assert(updatedPlan(6)(0) eq ConstFold)
64+
}
65+
66+
@Test
67+
def insertBeforeAfter = {
68+
object ConstFold extends TestPlugin {
69+
override val runsAfter = Set(classOf[P3d])
70+
override val runsBefore = Set(classOf[P7], classOf[P8])
71+
}
72+
73+
// prefers the runsBefore
74+
val updatedPlan = Plugins.schedule(basicPlan, ConstFold :: Nil, _ => Nil)
75+
assert(updatedPlan(6)(0) eq ConstFold)
76+
}
77+
78+
@Test
79+
def constraintUnsatisfiable = {
80+
object ConstFold extends TestPlugin {
81+
override val runsAfter = Set(classOf[P6d])
82+
override val runsBefore = Set(classOf[P2], classOf[P8])
83+
}
84+
85+
try {
86+
Plugins.schedule(basicPlan, ConstFold :: Nil, _ => Nil)
87+
assert(false, "unsatisfiable constraint should throw exception, but not")
88+
} catch {
89+
case _: Exception =>
90+
}
91+
}
92+
93+
@Test
94+
def orderingTwoPlugins1 = {
95+
object M1 extends TestPlugin {
96+
override val runsAfter = Set(classOf[P3d])
97+
override val runsBefore = Set(M2.getClass, classOf[P7], classOf[P8])
98+
}
99+
object M2 extends TestPlugin {
100+
override val runsAfter = Set(classOf[P3d])
101+
override val runsBefore = Set(classOf[P7], classOf[P8])
102+
}
103+
104+
// M1 inserted to plan first
105+
val updatedPlan1 = Plugins.schedule(basicPlan, M1 :: M2 :: Nil, _ => Nil)
106+
assert(updatedPlan1(6)(0) eq M1)
107+
assert(updatedPlan1(7)(0) eq M2)
108+
109+
// M2 inserted to plan first
110+
val updatedPlan2 = Plugins.schedule(basicPlan, M2 :: M1 :: Nil, _ => Nil)
111+
assert(updatedPlan2(6)(0) eq M1)
112+
assert(updatedPlan2(7)(0) eq M2)
113+
}
114+
115+
@Test
116+
def orderingTwoPlugins2 = {
117+
object M1 extends TestPlugin {
118+
override val runsAfter = Set(classOf[P3d], M2.getClass)
119+
}
120+
object M2 extends TestPlugin {
121+
override val runsAfter = Set(classOf[P3d])
122+
override val runsBefore = Set(classOf[P7], classOf[P8])
123+
}
124+
125+
// M1 inserted to plan first
126+
val updatedPlan1 = Plugins.schedule(basicPlan, M1 :: M2 :: Nil, _ => Nil)
127+
assert(updatedPlan1(4)(0) eq M1)
128+
assert(updatedPlan1(3)(0) eq M2)
129+
130+
// M2 inserted to plan first
131+
val updatedPlan2 = Plugins.schedule(basicPlan, M2 :: M1 :: Nil, _ => Nil)
132+
assert(updatedPlan2(7)(0) eq M1)
133+
assert(updatedPlan2(6)(0) eq M2)
134+
}
135+
}

0 commit comments

Comments
 (0)