Skip to content

Commit fa9a48e

Browse files
authored
Merge pull request #7787 from dotty-staging/scalajs-1.0.0-RC2
Upgrade to Scala.js 1.0.0-RC2.
2 parents 2994dfe + c99d407 commit fa9a48e

File tree

4 files changed

+199
-46
lines changed

4 files changed

+199
-46
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
427427

428428
val Flag_SYNTHETIC: Flags = Flags.Synthetic.bits
429429
val Flag_METHOD: Flags = Flags.Method.bits
430-
val ExcludedForwarderFlags: Flags = {
431-
Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic |
432-
Flags.Private | Flags.Macro
433-
}.bits
430+
val ExcludedForwarderFlags: Flags = DottyBackendInterface.ExcludedForwarderFlags.bits
434431

435432
def isQualifierSafeToElide(qual: Tree): Boolean = tpd.isIdempotentExpr(qual)
436433

@@ -1195,3 +1192,10 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
11951192

11961193
def currentUnit: CompilationUnit = ctx.compilationUnit
11971194
}
1195+
1196+
object DottyBackendInterface {
1197+
val ExcludedForwarderFlags: Flags.FlagSet = {
1198+
Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic |
1199+
Flags.Private | Flags.Macro
1200+
}
1201+
}

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

Lines changed: 184 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class JSCodeGen()(implicit ctx: Context) {
6767

6868
// Some state --------------------------------------------------------------
6969

70+
private val generatedClasses = mutable.ListBuffer.empty[js.ClassDef]
71+
7072
private val currentClassSym = new ScopedVar[Symbol]
7173
private val currentMethodSym = new ScopedVar[Symbol]
7274
private val localNames = new ScopedVar[LocalNameGenerator]
@@ -104,7 +106,11 @@ class JSCodeGen()(implicit ctx: Context) {
104106
// Compilation unit --------------------------------------------------------
105107

106108
def run(): Unit = {
107-
genCompilationUnit(ctx.compilationUnit)
109+
try {
110+
genCompilationUnit(ctx.compilationUnit)
111+
} finally {
112+
generatedClasses.clear()
113+
}
108114
}
109115

110116
/** Generates the Scala.js IR for a compilation unit
@@ -137,8 +143,6 @@ class JSCodeGen()(implicit ctx: Context) {
137143
}
138144
val allTypeDefs = collectTypeDefs(cunit.tpdTree)
139145

140-
val generatedClasses = mutable.ListBuffer.empty[(Symbol, js.ClassDef)]
141-
142146
// TODO Record anonymous JS function classes
143147

144148
/* Finally, we emit true code for the remaining class defs. */
@@ -167,20 +171,17 @@ class JSCodeGen()(implicit ctx: Context) {
167171
genScalaClass(td)
168172
}
169173

170-
generatedClasses += ((sym, tree))
174+
generatedClasses += tree
171175
}
172176
}
173177
}
174178

175-
val clDefs = generatedClasses.map(_._2).toList
176-
177-
for ((sym, tree) <- generatedClasses)
178-
genIRFile(cunit, sym, tree)
179+
for (tree <- generatedClasses)
180+
genIRFile(cunit, tree)
179181
}
180182

181-
private def genIRFile(cunit: CompilationUnit, sym: Symbol,
182-
tree: ir.Trees.ClassDef): Unit = {
183-
val outfile = getFileFor(cunit, sym, ".sjsir")
183+
private def genIRFile(cunit: CompilationUnit, tree: ir.Trees.ClassDef): Unit = {
184+
val outfile = getFileFor(cunit, tree.name.name, ".sjsir")
184185
val output = outfile.bufferedOutput
185186
try {
186187
ir.Serializers.serialize(output, tree)
@@ -189,21 +190,13 @@ class JSCodeGen()(implicit ctx: Context) {
189190
}
190191
}
191192

192-
private def getFileFor(cunit: CompilationUnit, sym: Symbol,
193+
private def getFileFor(cunit: CompilationUnit, className: ClassName,
193194
suffix: String): dotty.tools.io.AbstractFile = {
194-
import dotty.tools.io._
195-
196-
val outputDirectory: AbstractFile =
197-
ctx.settings.outputDir.value
198-
199-
val pathParts = sym.fullName.toString.split("[./]")
195+
val outputDirectory = ctx.settings.outputDir.value
196+
val pathParts = className.nameString.split('.')
200197
val dir = pathParts.init.foldLeft(outputDirectory)(_.subdirectoryNamed(_))
201-
202-
var filename = pathParts.last
203-
if (sym.is(ModuleClass))
204-
filename = filename + nme.MODULE_SUFFIX.toString
205-
206-
dir fileNamed (filename + suffix)
198+
val filename = pathParts.last
199+
dir.fileNamed(filename + suffix)
207200
}
208201

209202
// Generate a class --------------------------------------------------------
@@ -226,6 +219,7 @@ class JSCodeGen()(implicit ctx: Context) {
226219
}*/
227220

228221
val classIdent = encodeClassNameIdent(sym)
222+
val originalName = originalNameOfClass(sym)
229223
val isHijacked = false //isHijackedBoxedClass(sym)
230224

231225
// Optimizer hints
@@ -319,14 +313,51 @@ class JSCodeGen()(implicit ctx: Context) {
319313

320314
val staticInitializerStats = reflectInit.toList
321315
if (staticInitializerStats.nonEmpty)
322-
Some(genStaticInitializerWithStats(js.Block(staticInitializerStats)))
316+
List(genStaticInitializerWithStats(js.Block(staticInitializerStats)))
323317
else
324-
None
318+
Nil
319+
}
320+
321+
val allMemberDefsExceptStaticForwarders =
322+
generatedMembers ::: exports ::: optStaticInitializer
323+
324+
// Add static forwarders
325+
val allMemberDefs = if (!isCandidateForForwarders(sym)) {
326+
allMemberDefsExceptStaticForwarders
327+
} else {
328+
if (isStaticModule(sym)) {
329+
/* If the module class has no linked class, we must create one to
330+
* hold the static forwarders. Otherwise, this is going to be handled
331+
* when generating the companion class.
332+
*/
333+
if (!sym.linkedClass.exists) {
334+
val forwarders = genStaticForwardersFromModuleClass(Nil, sym)
335+
if (forwarders.nonEmpty) {
336+
val forwardersClassDef = js.ClassDef(
337+
js.ClassIdent(ClassName(classIdent.name.nameString.stripSuffix("$"))),
338+
originalName,
339+
ClassKind.Class,
340+
None,
341+
Some(js.ClassIdent(ir.Names.ObjectClass)),
342+
Nil,
343+
None,
344+
None,
345+
forwarders,
346+
Nil
347+
)(js.OptimizerHints.empty)
348+
generatedClasses += forwardersClassDef
349+
}
350+
}
351+
allMemberDefsExceptStaticForwarders
352+
} else {
353+
val forwarders = genStaticForwardersForClassOrInterface(
354+
allMemberDefsExceptStaticForwarders, sym)
355+
allMemberDefsExceptStaticForwarders ::: forwarders
356+
}
325357
}
326358

327359
// Hashed definitions of the class
328-
val hashedDefs =
329-
ir.Hashers.hashMemberDefs(generatedMembers ++ exports ++ optStaticInitializer)
360+
val hashedDefs = ir.Hashers.hashMemberDefs(allMemberDefs)
330361

331362
// The complete class definition
332363
val kind =
@@ -336,7 +367,7 @@ class JSCodeGen()(implicit ctx: Context) {
336367

337368
val classDefinition = js.ClassDef(
338369
classIdent,
339-
originalNameOfClass(sym),
370+
originalName,
340371
kind,
341372
None,
342373
Some(encodeClassNameIdent(sym.superClass)),
@@ -397,7 +428,7 @@ class JSCodeGen()(implicit ctx: Context) {
397428
*/
398429
private def genInterface(td: TypeDef): js.ClassDef = {
399430
val sym = td.symbol.asClass
400-
implicit val pos: Position = sym.span
431+
implicit val pos: SourcePosition = sym.sourcePos
401432

402433
val classIdent = encodeClassNameIdent(sym)
403434

@@ -418,9 +449,13 @@ class JSCodeGen()(implicit ctx: Context) {
418449

419450
val superInterfaces = genClassInterfaces(sym)
420451

452+
val genMethodsList = generatedMethods.toList
453+
val allMemberDefs =
454+
if (!isCandidateForForwarders(sym)) genMethodsList
455+
else genMethodsList ::: genStaticForwardersForClassOrInterface(genMethodsList, sym)
456+
421457
// Hashed definitions of the interface
422-
val hashedDefs =
423-
ir.Hashers.hashMemberDefs(generatedMethods.toList)
458+
val hashedDefs = ir.Hashers.hashMemberDefs(allMemberDefs)
424459

425460
js.ClassDef(
426461
classIdent,
@@ -446,6 +481,118 @@ class JSCodeGen()(implicit ctx: Context) {
446481
}
447482
}
448483

484+
// Static forwarders -------------------------------------------------------
485+
486+
/* This mimics the logic in BCodeHelpers.addForwarders and the code that
487+
* calls it, except that we never have collisions with existing methods in
488+
* the companion class. This is because in the IR, only methods with the
489+
* same `MethodName` (including signature) and that are also
490+
* `PublicStatic` would collide. There should never be an actual collision
491+
* because the only `PublicStatic` methods that are otherwise generated are
492+
* the bodies of SAMs, which have mangled names. If that assumption is
493+
* broken, an error message is emitted asking the user to report a bug.
494+
*
495+
* It is important that we always emit forwarders, because some Java APIs
496+
* actually have a public static method and a public instance method with
497+
* the same name. For example the class `Integer` has a
498+
* `def hashCode(): Int` and a `static def hashCode(Int): Int`. The JVM
499+
* back-end considers them as colliding because they have the same name,
500+
* but we must not.
501+
*/
502+
503+
/** Is the given Scala class, interface or module class a candidate for
504+
* static forwarders?
505+
*/
506+
def isCandidateForForwarders(sym: Symbol): Boolean = {
507+
// it must be a top level class
508+
sym.isStatic
509+
}
510+
511+
/** Gen the static forwarders to the members of a class or interface for
512+
* methods of its companion object.
513+
*
514+
* This is only done if there exists a companion object and it is not a JS
515+
* type.
516+
*
517+
* Precondition: `isCandidateForForwarders(sym)` is true
518+
*/
519+
def genStaticForwardersForClassOrInterface(
520+
existingMembers: List[js.MemberDef], sym: Symbol)(
521+
implicit pos: SourcePosition): List[js.MemberDef] = {
522+
val module = sym.companionModule
523+
if (!module.exists) {
524+
Nil
525+
} else {
526+
val moduleClass = module.moduleClass
527+
if (!isJSType(moduleClass))
528+
genStaticForwardersFromModuleClass(existingMembers, moduleClass)
529+
else
530+
Nil
531+
}
532+
}
533+
534+
/** Gen the static forwarders for the methods of a module class.
535+
*
536+
* Precondition: `isCandidateForForwarders(moduleClass)` is true
537+
*/
538+
def genStaticForwardersFromModuleClass(existingMembers: List[js.MemberDef],
539+
moduleClass: Symbol)(
540+
implicit pos: SourcePosition): List[js.MemberDef] = {
541+
542+
assert(moduleClass.is(ModuleClass), moduleClass)
543+
544+
val existingPublicStaticMethodNames = existingMembers.collect {
545+
case js.MethodDef(flags, name, _, _, _, _)
546+
if flags.namespace == js.MemberNamespace.PublicStatic =>
547+
name.name
548+
}.toSet
549+
550+
val members = {
551+
import dotty.tools.backend.jvm.DottyBackendInterface.ExcludedForwarderFlags
552+
moduleClass.info.membersBasedOnFlags(required = Flags.Method,
553+
excluded = ExcludedForwarderFlags).map(_.symbol)
554+
}
555+
556+
def isExcluded(m: Symbol): Boolean = {
557+
def hasAccessBoundary = m.accessBoundary(defn.RootClass) ne defn.RootClass
558+
m.is(Deferred) || m.isConstructor || hasAccessBoundary || (m.owner eq defn.ObjectClass)
559+
}
560+
561+
val forwarders = for {
562+
m <- members
563+
if !isExcluded(m)
564+
} yield {
565+
withNewLocalNameScope {
566+
val flags = js.MemberFlags.empty.withNamespace(js.MemberNamespace.PublicStatic)
567+
val methodIdent = encodeMethodSym(m)
568+
val originalName = originalNameOfMethod(m)
569+
val jsParams = for {
570+
(paramName, paramInfo) <- m.info.paramNamess.flatten.zip(m.info.paramInfoss.flatten)
571+
} yield {
572+
js.ParamDef(freshLocalIdent(paramName), NoOriginalName,
573+
toIRType(paramInfo), mutable = false, rest = false)
574+
}
575+
val resultType = toIRType(m.info.resultType)
576+
577+
if (existingPublicStaticMethodNames.contains(methodIdent.name)) {
578+
ctx.error(
579+
"Unexpected situation: found existing public static method " +
580+
s"${methodIdent.name.nameString} in the companion class of " +
581+
s"${moduleClass.fullName}; cannot generate a static forwarder " +
582+
"the method of the same name in the object." +
583+
"Please report this as a bug in the Scala.js support in dotty.",
584+
pos)
585+
}
586+
587+
js.MethodDef(flags, methodIdent, originalName, jsParams, resultType, Some {
588+
genApplyMethod(genLoadModule(moduleClass), m, jsParams.map(_.ref))
589+
})(OptimizerHints.empty, None)
590+
}
591+
}
592+
593+
forwarders.toList
594+
}
595+
449596
// Generate the fields of a class ------------------------------------------
450597

451598
/** Gen definitions for the fields of a class.
@@ -1316,14 +1463,12 @@ class JSCodeGen()(implicit ctx: Context) {
13161463
args: List[js.Tree])(implicit pos: SourcePosition): js.Tree = {
13171464

13181465
val className = encodeClassName(clazz)
1319-
val moduleClass = clazz.companionModule.moduleClass
1320-
13211466
val initName = encodeMethodSym(ctor).name
13221467
val newName = MethodName(newSimpleMethodName, initName.paramTypeRefs,
13231468
jstpe.ClassRef(className))
13241469
val newMethodIdent = js.MethodIdent(newName)
13251470

1326-
js.Apply(js.ApplyFlags.empty, genLoadModule(moduleClass), newMethodIdent, args)(
1471+
js.ApplyStatic(js.ApplyFlags.empty, className, newMethodIdent, args)(
13271472
jstpe.ClassType(className))
13281473
}
13291474

@@ -1689,7 +1834,7 @@ class JSCodeGen()(implicit ctx: Context) {
16891834
} else externalEquals
16901835
// scalastyle:on line.size.limit
16911836
}
1692-
genModuleApplyMethod(equalsMethod, List(lsrc, rsrc))
1837+
genApplyStatic(equalsMethod, List(lsrc, rsrc))
16931838
} else {
16941839
// if (lsrc eq null) rsrc eq null else lsrc.equals(rsrc)
16951840
if (lsym == defn.StringClass) {
@@ -2738,9 +2883,9 @@ class JSCodeGen()(implicit ctx: Context) {
27382883
} else if (sym == defn.BoxedUnit_TYPE) {
27392884
js.ClassOf(jstpe.VoidRef)
27402885
} else {
2741-
val inst = genLoadModule(sym.owner)
2886+
val className = encodeClassName(sym.owner)
27422887
val method = encodeStaticMemberSym(sym)
2743-
js.Apply(js.ApplyFlags.empty, inst, method, Nil)(toIRType(sym.info))
2888+
js.ApplyStatic(js.ApplyFlags.empty, className, method, Nil)(toIRType(sym.info))
27442889
}
27452890
}
27462891

@@ -2920,7 +3065,7 @@ class JSCodeGen()(implicit ctx: Context) {
29203065
}
29213066

29223067
private def isMethodStaticInIR(sym: Symbol): Boolean =
2923-
sym.is(JavaStatic, butNot = JavaDefined)
3068+
sym.is(JavaStatic)
29243069

29253070
/** Generate a Class[_] value (e.g. coming from classOf[T]) */
29263071
private def genClassConstant(tpe: Type)(implicit pos: Position): js.Tree =

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,17 @@ object JSEncoding {
237237
js.ClassIdent(encodeClassName(sym))
238238

239239
def encodeClassName(sym: Symbol)(implicit ctx: Context): ClassName = {
240-
if (sym == defn.BoxedUnitClass) {
240+
val sym1 =
241+
if (sym.isAllOf(ModuleClass | JavaDefined)) sym.linkedClass
242+
else sym
243+
244+
if (sym1 == defn.BoxedUnitClass) {
241245
/* Rewire scala.runtime.BoxedUnit to java.lang.Void, as the IR expects.
242246
* BoxedUnit$ is a JVM artifact.
243247
*/
244248
ir.Names.BoxedUnitClass
245249
} else {
246-
ClassName(sym.fullName.mangledString)
250+
ClassName(sym1.fullName.mangledString)
247251
}
248252
}
249253

project/plugins.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// e.g. addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0")
44

5-
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.0-RC1")
5+
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.0-RC2")
66

77
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.6")
88

0 commit comments

Comments
 (0)