Skip to content

Commit 6871ce6

Browse files
committed
Address the review.
1 parent d1fb805 commit 6871ce6

File tree

4 files changed

+110
-90
lines changed

4 files changed

+110
-90
lines changed

compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,40 @@ final class JSDefinitions()(implicit ctx: Context) {
189189
def isJSThisFunctionClass(cls: Symbol): Boolean =
190190
isScalaJSVarArityClass(cls, "ThisFunction")
191191

192+
/** Definitions related to the treatment of JUnit boostrappers. */
193+
object junit {
194+
lazy val TestAnnotType: TypeRef = ctx.requiredClassRef("org.junit.Test")
195+
def TestAnnotClass(implicit ctx: Context): ClassSymbol = TestAnnotType.symbol.asClass
196+
197+
lazy val BeforeAnnotType: TypeRef = ctx.requiredClassRef("org.junit.Before")
198+
def BeforeAnnotClass(implicit ctx: Context): ClassSymbol = BeforeAnnotType.symbol.asClass
199+
200+
lazy val AfterAnnotType: TypeRef = ctx.requiredClassRef("org.junit.After")
201+
def AfterAnnotClass(implicit ctx: Context): ClassSymbol = AfterAnnotType.symbol.asClass
202+
203+
lazy val BeforeClassAnnotType: TypeRef = ctx.requiredClassRef("org.junit.BeforeClass")
204+
def BeforeClassAnnotClass(implicit ctx: Context): ClassSymbol = BeforeClassAnnotType.symbol.asClass
205+
206+
lazy val AfterClassAnnotType: TypeRef = ctx.requiredClassRef("org.junit.AfterClass")
207+
def AfterClassAnnotClass(implicit ctx: Context): ClassSymbol = AfterClassAnnotType.symbol.asClass
208+
209+
lazy val IgnoreAnnotType: TypeRef = ctx.requiredClassRef("org.junit.Ignore")
210+
def IgnoreAnnotClass(implicit ctx: Context): ClassSymbol = IgnoreAnnotType.symbol.asClass
211+
212+
lazy val BootstrapperType: TypeRef = ctx.requiredClassRef("org.scalajs.junit.Bootstrapper")
213+
214+
lazy val TestMetadataType: TypeRef = ctx.requiredClassRef("org.scalajs.junit.TestMetadata")
215+
216+
lazy val NoSuchMethodExceptionType: TypeRef = ctx.requiredClassRef("java.lang.NoSuchMethodException")
217+
218+
lazy val FutureType: TypeRef = ctx.requiredClassRef("scala.concurrent.Future")
219+
def FutureClass(implicit ctx: Context): ClassSymbol = FutureType.symbol.asClass
220+
221+
private lazy val FutureModule_successfulR = ctx.requiredModule("scala.concurrent.Future").requiredMethodRef("successful")
222+
def FutureModule_successful(implicit ctx: Context): Symbol = FutureModule_successfulR.symbol
223+
224+
private lazy val SuccessModule_applyR = ctx.requiredModule("scala.util.Success").requiredMethodRef(nme.apply)
225+
def SuccessModule_apply(implicit ctx: Context): Symbol = SuccessModule_applyR.symbol
226+
}
227+
192228
}

compiler/src/dotty/tools/backend/sjs/JUnitBootstrappers.scala

Lines changed: 66 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -106,71 +106,23 @@ import dotty.tools.dotc.transform.MegaPhase._
106106
* some point in the future.
107107
*/
108108
class JUnitBootstrappers extends MiniPhase {
109+
import JUnitBootstrappers._
109110
import ast.tpd._
111+
import JSDefinitions.jsdefn
110112

111113
def phaseName: String = "junitBootstrappers"
112114

113115
override def isEnabled(implicit ctx: Context): Boolean =
114116
super.isEnabled && ctx.settings.scalajs.value
115117

116-
private object Names {
117-
val beforeClass: TermName = termName("beforeClass")
118-
val afterClass: TermName = termName("afterClass")
119-
val before: TermName = termName("before")
120-
val after: TermName = termName("after")
121-
val tests: TermName = termName("tests")
122-
val invokeTest: TermName = termName("invokeTest")
123-
val newInstance: TermName = termName("newInstance")
124-
125-
val instance: TermName = termName("instance")
126-
val name: TermName = termName("name")
127-
val castInstance: TermName = termName("castInstance")
128-
}
129-
130-
private class MyDefinitions()(implicit ctx: Context) {
131-
lazy val TestAnnotType: TypeRef = ctx.requiredClassRef("org.junit.Test")
132-
def TestAnnotClass(implicit ctx: Context): ClassSymbol = TestAnnotType.symbol.asClass
133-
134-
lazy val BeforeAnnotType: TypeRef = ctx.requiredClassRef("org.junit.Before")
135-
def BeforeAnnotClass(implicit ctx: Context): ClassSymbol = BeforeAnnotType.symbol.asClass
136-
137-
lazy val AfterAnnotType: TypeRef = ctx.requiredClassRef("org.junit.After")
138-
def AfterAnnotClass(implicit ctx: Context): ClassSymbol = AfterAnnotType.symbol.asClass
139-
140-
lazy val BeforeClassAnnotType: TypeRef = ctx.requiredClassRef("org.junit.BeforeClass")
141-
def BeforeClassAnnotClass(implicit ctx: Context): ClassSymbol = BeforeClassAnnotType.symbol.asClass
142-
143-
lazy val AfterClassAnnotType: TypeRef = ctx.requiredClassRef("org.junit.AfterClass")
144-
def AfterClassAnnotClass(implicit ctx: Context): ClassSymbol = AfterClassAnnotType.symbol.asClass
145-
146-
lazy val IgnoreAnnotType: TypeRef = ctx.requiredClassRef("org.junit.Ignore")
147-
def IgnoreAnnotClass(implicit ctx: Context): ClassSymbol = IgnoreAnnotType.symbol.asClass
148-
149-
lazy val BootstrapperType: TypeRef = ctx.requiredClassRef("org.scalajs.junit.Bootstrapper")
150-
151-
lazy val TestMetadataType: TypeRef = ctx.requiredClassRef("org.scalajs.junit.TestMetadata")
152-
153-
lazy val NoSuchMethodExceptionType: TypeRef = ctx.requiredClassRef("java.lang.NoSuchMethodException")
154-
155-
lazy val FutureType: TypeRef = ctx.requiredClassRef("scala.concurrent.Future")
156-
def FutureClass(implicit ctx: Context): ClassSymbol = FutureType.symbol.asClass
157-
158-
private lazy val FutureModule_successfulR = ctx.requiredModule("scala.concurrent.Future").requiredMethodRef("successful")
159-
def FutureModule_successful(implicit ctx: Context): Symbol = FutureModule_successfulR.symbol
160-
161-
private lazy val SuccessModule_applyR = ctx.requiredModule("scala.util.Success").requiredMethodRef(nme.apply)
162-
def SuccessModule_apply(implicit ctx: Context): Symbol = SuccessModule_applyR.symbol
163-
}
164-
165118
// The actual transform -------------------------------
166119

167120
override def transformPackageDef(tree: PackageDef)(implicit ctx: Context): Tree = {
168-
// TODO Can we cache this better?
169-
implicit val mydefn: MyDefinitions = new MyDefinitions()
121+
val junitdefn = jsdefn.junit
170122

171123
@tailrec
172124
def hasTests(sym: ClassSymbol): Boolean = {
173-
sym.info.decls.exists(m => m.is(Method) && m.hasAnnotation(mydefn.TestAnnotClass)) ||
125+
sym.info.decls.exists(m => m.is(Method) && m.hasAnnotation(junitdefn.TestAnnotClass)) ||
174126
sym.superClass.exists && hasTests(sym.superClass.asClass)
175127
}
176128

@@ -190,32 +142,39 @@ class JUnitBootstrappers extends MiniPhase {
190142
}
191143

192144
private def genBootstrapper(testClass: ClassSymbol)(
193-
implicit ctx: Context, mydefn: MyDefinitions): TypeDef = {
145+
implicit ctx: Context): TypeDef = {
146+
147+
val junitdefn = jsdefn.junit
148+
149+
/* The name of the boostrapper module. It is derived from the test class name by
150+
* appending a specific suffix string mandated "by spec". It will indeed also be
151+
* computed as such at run-time by the Scala.js JUnit Runtime support. Therefore,
152+
* it must *not* be a dotc semantic name.
153+
*/
154+
val bootstrapperName = (testClass.name ++ "$scalajs$junit$bootstrapper").toTermName
194155

195156
val owner = testClass.owner
196-
val moduleSym = ctx.newCompleteModuleSymbol(owner,
197-
(testClass.name ++ "$scalajs$junit$bootstrapper").toTermName,
157+
val moduleSym = ctx.newCompleteModuleSymbol(owner, bootstrapperName,
198158
Synthetic, Synthetic,
199-
List(defn.ObjectType, mydefn.BootstrapperType), newScope,
159+
List(defn.ObjectType, junitdefn.BootstrapperType), newScope,
200160
coord = testClass.span, assocFile = testClass.assocFile).entered
201161
val classSym = moduleSym.moduleClass.asClass
202162

203163
val constr = genConstructor(classSym)
204164

205-
val testMethods = annotatedMethods(testClass, mydefn.TestAnnotClass)
165+
val testMethods = annotatedMethods(testClass, junitdefn.TestAnnotClass)
206166

207167
val defs = List(
208-
genCallOnModule(classSym, Names.beforeClass, testClass.companionModule, mydefn.BeforeClassAnnotClass),
209-
genCallOnModule(classSym, Names.afterClass, testClass.companionModule, mydefn.AfterClassAnnotClass),
210-
genCallOnParam(classSym, Names.before, testClass, mydefn.BeforeAnnotClass),
211-
genCallOnParam(classSym, Names.after, testClass, mydefn.AfterAnnotClass),
168+
genCallOnModule(classSym, junitNme.beforeClass, testClass.companionModule, junitdefn.BeforeClassAnnotClass),
169+
genCallOnModule(classSym, junitNme.afterClass, testClass.companionModule, junitdefn.AfterClassAnnotClass),
170+
genCallOnParam(classSym, junitNme.before, testClass, junitdefn.BeforeAnnotClass),
171+
genCallOnParam(classSym, junitNme.after, testClass, junitdefn.AfterAnnotClass),
212172
genTests(classSym, testMethods),
213173
genInvokeTest(classSym, testClass, testMethods),
214174
genNewInstance(classSym, testClass)
215175
)
216176

217-
if (ctx.sbtCallback != null)
218-
sbt.APIUtils.registerDummyClass(classSym)
177+
sbt.APIUtils.registerDummyClass(classSym)
219178

220179
ClassDef(classSym, constr, defs)
221180
}
@@ -247,7 +206,7 @@ class JUnitBootstrappers extends MiniPhase {
247206

248207
private def genCallOnParam(owner: ClassSymbol, name: TermName, testClass: ClassSymbol, annot: Symbol)(implicit ctx: Context): DefDef = {
249208
val sym = ctx.newSymbol(owner, name, Synthetic | Method,
250-
MethodType(Names.instance :: Nil, defn.ObjectType :: Nil, defn.UnitType)).entered
209+
MethodType(junitNme.instance :: Nil, defn.ObjectType :: Nil, defn.UnitType)).entered
251210

252211
DefDef(sym, { (paramRefss: List[List[Tree]]) =>
253212
val List(List(instanceParamRef)) = paramRefss
@@ -258,41 +217,45 @@ class JUnitBootstrappers extends MiniPhase {
258217
}
259218

260219
private def genTests(owner: ClassSymbol, tests: List[Symbol])(
261-
implicit ctx: Context, mydefn: MyDefinitions): DefDef = {
220+
implicit ctx: Context): DefDef = {
221+
222+
val junitdefn = jsdefn.junit
262223

263-
val sym = ctx.newSymbol(owner, Names.tests, Synthetic | Method,
264-
MethodType(Nil, defn.ArrayOf(mydefn.TestMetadataType))).entered
224+
val sym = ctx.newSymbol(owner, junitNme.tests, Synthetic | Method,
225+
MethodType(Nil, defn.ArrayOf(junitdefn.TestMetadataType))).entered
265226

266227
DefDef(sym, {
267228
val metadata = for (test <- tests) yield {
268229
val name = Literal(Constant(test.name.toString))
269-
val ignored = Literal(Constant(test.hasAnnotation(mydefn.IgnoreAnnotClass)))
230+
val ignored = Literal(Constant(test.hasAnnotation(junitdefn.IgnoreAnnotClass)))
270231
// TODO Handle @Test annotations with arguments
271232
// val reifiedAnnot = New(mydefn.TestAnnotType, test.getAnnotation(mydefn.TestAnnotClass).get.arguments)
272-
val testAnnot = test.getAnnotation(mydefn.TestAnnotClass).get
233+
val testAnnot = test.getAnnotation(junitdefn.TestAnnotClass).get
273234
if (testAnnot.arguments.nonEmpty)
274235
ctx.error("@Test annotations with arguments are not yet supported in Scala.js for dotty", testAnnot.tree.sourcePos)
275-
val noArgConstr = mydefn.TestAnnotType.member(nme.CONSTRUCTOR).suchThat(_.info.paramInfoss.head.isEmpty).symbol.asTerm
276-
val reifiedAnnot = New(mydefn.TestAnnotType, noArgConstr, Nil)
277-
New(mydefn.TestMetadataType, List(name, ignored, reifiedAnnot))
236+
val noArgConstr = junitdefn.TestAnnotType.member(nme.CONSTRUCTOR).suchThat(_.info.paramInfoss.head.isEmpty).symbol.asTerm
237+
val reifiedAnnot = New(junitdefn.TestAnnotType, noArgConstr, Nil)
238+
New(junitdefn.TestMetadataType, List(name, ignored, reifiedAnnot))
278239
}
279-
JavaSeqLiteral(metadata, TypeTree(mydefn.TestMetadataType))
240+
JavaSeqLiteral(metadata, TypeTree(junitdefn.TestMetadataType))
280241
})
281242
}
282243

283244
private def genInvokeTest(owner: ClassSymbol, testClass: ClassSymbol, tests: List[Symbol])(
284-
implicit ctx: Context, mydefn: MyDefinitions): DefDef = {
245+
implicit ctx: Context): DefDef = {
246+
247+
val junitdefn = jsdefn.junit
285248

286-
val sym = ctx.newSymbol(owner, Names.invokeTest, Synthetic | Method,
287-
MethodType(List(Names.instance, Names.name), List(defn.ObjectType, defn.StringType), mydefn.FutureType)).entered
249+
val sym = ctx.newSymbol(owner, junitNme.invokeTest, Synthetic | Method,
250+
MethodType(List(junitNme.instance, junitNme.name), List(defn.ObjectType, defn.StringType), junitdefn.FutureType)).entered
288251

289252
DefDef(sym, { (paramRefss: List[List[Tree]]) =>
290253
val List(List(instanceParamRef, nameParamRef)) = paramRefss
291-
val castInstanceSym = ctx.newSymbol(sym, Names.castInstance, Synthetic, testClass.typeRef, coord = owner.span)
254+
val castInstanceSym = ctx.newSymbol(sym, junitNme.castInstance, Synthetic, testClass.typeRef, coord = owner.span)
292255
Block(
293256
ValDef(castInstanceSym, instanceParamRef.cast(testClass.typeRef)) :: Nil,
294257
tests.foldRight[Tree] {
295-
val tp = mydefn.NoSuchMethodExceptionType
258+
val tp = junitdefn.NoSuchMethodExceptionType
296259
val constr = tp.member(nme.CONSTRUCTOR).suchThat { c =>
297260
c.info.paramInfoss.head.size == 1 &&
298261
c.info.paramInfoss.head.head.isRef(defn.StringClass)
@@ -308,16 +271,18 @@ class JUnitBootstrappers extends MiniPhase {
308271
}
309272

310273
private def genTestInvocation(testClass: ClassSymbol, testMethod: Symbol, instance: Tree)(
311-
implicit ctx: Context, mydefn: MyDefinitions): Tree = {
274+
implicit ctx: Context): Tree = {
275+
276+
val junitdefn = jsdefn.junit
312277

313278
val resultType = testMethod.info.resultType
314279
if (resultType.isRef(defn.UnitClass)) {
315-
val newSuccess = ref(mydefn.SuccessModule_apply).appliedTo(ref(defn.BoxedUnit_UNIT))
280+
val newSuccess = ref(junitdefn.SuccessModule_apply).appliedTo(ref(defn.BoxedUnit_UNIT))
316281
Block(
317282
instance.select(testMethod).appliedToNone :: Nil,
318-
ref(mydefn.FutureModule_successful).appliedTo(newSuccess)
283+
ref(junitdefn.FutureModule_successful).appliedTo(newSuccess)
319284
)
320-
} else if (resultType.isRef(mydefn.FutureClass)) {
285+
} else if (resultType.isRef(junitdefn.FutureClass)) {
321286
instance.select(testMethod).appliedToNone
322287
} else {
323288
// We lie in the error message to not expose that we support async testing.
@@ -327,7 +292,7 @@ class JUnitBootstrappers extends MiniPhase {
327292
}
328293

329294
private def genNewInstance(owner: ClassSymbol, testClass: ClassSymbol)(implicit ctx: Context): DefDef = {
330-
val sym = ctx.newSymbol(owner, Names.newInstance, Synthetic | Method,
295+
val sym = ctx.newSymbol(owner, junitNme.newInstance, Synthetic | Method,
331296
MethodType(Nil, defn.ObjectType)).entered
332297

333298
DefDef(sym, New(testClass.typeRef, Nil))
@@ -339,3 +304,21 @@ class JUnitBootstrappers extends MiniPhase {
339304
private def annotatedMethods(owner: ClassSymbol, annot: Symbol)(implicit ctx: Context): List[Symbol] =
340305
owner.info.decls.filter(m => m.is(Method) && m.hasAnnotation(annot))
341306
}
307+
308+
object JUnitBootstrappers {
309+
310+
private object junitNme {
311+
val beforeClass: TermName = termName("beforeClass")
312+
val afterClass: TermName = termName("afterClass")
313+
val before: TermName = termName("before")
314+
val after: TermName = termName("after")
315+
val tests: TermName = termName("tests")
316+
val invokeTest: TermName = termName("invokeTest")
317+
val newInstance: TermName = termName("newInstance")
318+
319+
val instance: TermName = termName("instance")
320+
val name: TermName = termName("name")
321+
val castInstance: TermName = termName("castInstance")
322+
}
323+
324+
}

compiler/src/dotty/tools/dotc/sbt/APIUtils.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Symbols._
1313
import NameOps._
1414

1515
import xsbti.api
16+
import xsbti.api.SafeLazy.strict
1617

1718
/** Utilities to deal with xsbti.api.
1819
*
@@ -22,7 +23,7 @@ object APIUtils {
2223
private object Constants {
2324
val PublicAccess = api.Public.create()
2425
val EmptyModifiers = new api.Modifiers(false, false, false, false, false, false, false, false)
25-
val EmptyStructure = api.Structure.of(lzy(Array.empty), lzy(Array.empty), lzy(Array.empty))
26+
val EmptyStructure = api.Structure.of(strict(Array.empty), strict(Array.empty), strict(Array.empty))
2627
val EmptyType = api.EmptyType.of()
2728
}
2829

@@ -37,8 +38,10 @@ object APIUtils {
3738
* a dummy empty class can be registered instead, using this method.
3839
*/
3940
def registerDummyClass(classSym: ClassSymbol)(implicit ctx: Context): Unit = {
40-
val classLike = emptyClassLike(classSym)
41-
ctx.sbtCallback.api(ctx.compilationUnit.source.file.file, classLike)
41+
if (ctx.sbtCallback != null) {
42+
val classLike = emptyClassLike(classSym)
43+
ctx.sbtCallback.api(ctx.compilationUnit.source.file.file, classLike)
44+
}
4245
}
4346

4447
// See APIUtils.emptyClassLike
@@ -50,8 +53,6 @@ object APIUtils {
5053
else api.DefinitionType.ClassDef
5154
val topLevel = classSym.isTopLevelClass
5255
api.ClassLike.of(name, PublicAccess, EmptyModifiers, Array.empty, definitionType,
53-
lzy(EmptyType), lzy(EmptyStructure), Array.empty, Array.empty, topLevel, Array.empty)
56+
strict(EmptyType), strict(EmptyStructure), Array.empty, Array.empty, topLevel, Array.empty)
5457
}
55-
56-
private def lzy[T <: AnyRef](t: T): api.Lazy[T] = api.SafeLazy.strict(t)
5758
}

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ object Build {
837837
_.filter(!_.name.startsWith("junit-interface"))
838838
},
839839
libraryDependencies +=
840-
"org.scala-js" % "scalajs-junit-test-runtime_2.12" % scalaJSVersion % "test",
840+
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion % "test").withDottyCompat(scalaVersion.value),
841841

842842
// The main class cannot be found automatically due to the empty inc.Analysis
843843
mainClass in Compile := Some("hello.HelloWorld"),

0 commit comments

Comments
 (0)