10000 Get rid of `TopLevelConstructorExportDef`s in the IR. · scala-js/scala-js@ce17362 · GitHub
[go: up one dir, main page]

Skip to content

Commit ce17362

Browse files
committed
Get rid of TopLevelConstructorExportDefs in the IR.
Exports of Scala constructors can be directly encoded as top-level static *functions* instead, since what they did was creating a new instance inside of the desugared body anyway. Therefore, this commit gets rid of them at the IR level, by moving their desugaring from the `ClassEmitter` all the way back to the compiler. The only piece of observable semantics we lose by doing so is that a constructor export def did set the `prototype` of the function to `$c_LTheClass.prototype`, indirectly allowing JS code to use `x instanceof ExportedCtor` to perform a Scala instance test. However, this "feature" was never documented, nor tested.
1 parent ce0a278 commit ce17362

File tree

12 files changed

+35
-139
lines changed

12 files changed

+35
-139
lines changed

compiler/src/main/scala/org/scalajs/nscplugin/GenJSExports.scala

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ trait GenJSExports extends SubComponent { self: GenJSCode =>
6161
}
6262

6363
def genConstructorExports(
64-
classSym: Symbol): List[js.TopLevelConstructorExportDef] = {
64+
classSym: Symbol): List[js.TopLevelMethodExportDef] = {
6565

6666
val constructors = classSym.tpe.member(nme.CONSTRUCTOR).alternatives
6767

@@ -81,11 +81,11 @@ trait GenJSExports extends SubComponent { self: GenJSCode =>
8181

8282
implicit val pos = ctors.head.pos
8383

84-
val js.MethodDef(_, _, args, _, body) =
85-
withNewLocalNameScope(genExportMethod(ctors, JSName.Literal(jsName),
86-
static = false))
84+
val methodDef = withNewLocalNameScope {
85+
genExportMethod(ctors, JSName.Literal(jsName), static = true)
86+
}
8787

88-
js.TopLevelConstructorExportDef(jsName, args, body.get)
88+
js.TopLevelMethodExportDef(methodDef)
8989
}
9090

9191
exports.toList
@@ -777,27 +777,32 @@ trait GenJSExports extends SubComponent { self: GenJSCode =>
777777

778778
/** Generate the final forwarding call to the exported method. */
779779
private def genResult(exported: Exported, args: List[js.Tree],
780-
static: Boolean)(implicit pos: Position) = {
780+
static: Boolean)(implicit pos: Position): js.Tree = {
781781
val sym = exported.sym
782-
val thisType =
783-
if (sym.owner == ObjectClass) jstpe.ClassType(ir.Definitions.ObjectClass)
784-
else encodeClassType(sym.owner)
785-
val receiver =
786-
if (static) genLoadModule(sym.owner)
787-
else js.This()(thisType)
788-
val call = {
789-
if (isNonNativeJSClass(currentClassSym)) {
790-
assert(sym.owner == currentClassSym.get, sym.fullName)
791-
genApplyJSClassMethod(receiver, sym, args)
792-
} else {
793-
if (sym.isClassConstructor)
794-
genApplyMethodStatically(receiver, sym, args)
795-
else
796-
genApplyMethod(receiver, sym, args)
797-
}
782+
783+
def receiver = {
784+
if (static)
785+
genLoadModule(sym.owner)
786+
else if (sym.owner == ObjectClass)
787+
js.This()(jstpe.ClassType(ir.Definitions.ObjectClass))
788+
else
789+
js.This()(encodeClassType(sym.owner))
790+
}
791+
792+
def boxIfNeeded(call: js.Tree): js.Tree = {
793+
ensureBoxed(call,
794+
enteringPhase(currentRun.posterasurePhase)(sym.tpe.resultType))
795+
}
796+
797+
if (isNonNativeJSClass(currentClassSym)) {
798+
assert(sym.owner == currentClassSym.get, sym.fullName)
799+
boxIfNeeded(genApplyJSClassMethod(receiver, sym, args))
800+
} else {
801+
if (sym.isClassConstructor)
802+
genNew(currentClassSym, sym, args)
803+
else
804+
boxIfNeeded(genApplyMethod(receiver, sym, args))
798805
}
799-
ensureBoxed(call,
800-
enteringPhase(currentRun.posterasurePhase)(sym.tpe.resultType))
801806
}
802807

803808
private final class ParamSpec(val sym: Symbol, val tpe: Type,

ir/src/main/scala/org/scalajs/ir/Printers.scala

Lines changed: 0 additions & 7 deletions
F438
Original file line numberDiff line numberDiff line change
@@ -912,13 +912,6 @@ object Printers {
912912

913913
def print(topLevelExportDef: TopLevelExportDef): Unit = {
914914
topLevelExportDef match {
915-
case TopLevelConstructorExportDef(fullName, args, body) =>
916-
print("export top constructor \"")
917-
printEscapeJS(fullName, out)
918-
print('\"')
919-
printSig(args, NoType) // NoType as trick not to display a type
920-
printBlock(body)
921-
922915
case TopLevelJSClassExportDef(fullName) =>
923916
print("export top class \"")
924917
printEscapeJS(fullName, out)

ir/src/main/scala/org/scalajs/ir/Serializers.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -533,10 +533,6 @@ object Serializers {
533533
import buffer._
534534
writePosition(topLevelExportDef.pos)
535535
topLevelExportDef match {
536-
case TopLevelConstructorExportDef(fullName, args, body) =>
537-
writeByte(TagTopLevelConstructorExportDef)
538-
writeString(fullName); writeParamDefs(args); writeTree(body)
539-
540536
case TopLevelJSClassExportDef(fullName) =>
541537
writeByte(TagTopLevelJSClassExportDef)
542538
writeString(fullName)
@@ -996,10 +992,6 @@ object Serializers {
996992
val tag = input.readByte()
997993

998994
(tag: @switch) match {
999-
case TagTopLevelConstructorExportDef =>
1000-
TopLevelConstructorExportDef(readString(), readParamDefs(),
1001-
readTree())
1002-
1003995
case TagTopLevelJSClassExportDef => TopLevelJSClassExportDef(readString())
1004996
case TagTopLevelModuleExportDef => TopLevelModuleExportDef(readString())
1005997
case TagTopLevelMethodExportDef => TopLevelMethodExportDef(readMemberDef().asInstanceOf[MethodDef])

ir/src/main/scala/org/scalajs/ir/Tags.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,7 @@ private[ir] object Tags {
105105

106106
// Tags for top-level export defs
107107

108-
final val TagTopLevelConstructorExportDef = 1
109-
final val TagTopLevelJSClassExportDef = TagTopLevelConstructorExportDef + 1
108+
final val TagTopLevelJSClassExportDef = 1
110109
final val TagTopLevelModuleExportDef = TagTopLevelJSClassExportDef + 1
111110
final val TagTopLevelMethodExportDef = TagTopLevelModuleExportDef + 1
112111
final val TagTopLevelFieldExportDef = TagTopLevelMethodExportDef + 1

ir/src/main/scala/org/scalajs/ir/Transformers.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,6 @@ object Transformers {
250250
implicit val pos = exportDef.pos
251251

252252
exportDef match {
253-
case TopLevelConstructorExportDef(fullName, args, body) =>
254-
TopLevelConstructorExportDef(fullName, args, transformStat(body))
255-
256253
case _:TopLevelJSClassExportDef | _:TopLevelModuleExportDef |
257254
_:TopLevelFieldExportDef =>
258255
exportDef

ir/src/main/scala/org/scalajs/ir/Traversers.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,6 @@ object Traversers {
230230

231231
def traverseTopLevelExportDef(exportDef: TopLevelExportDef): Unit = {
232232
exportDef match {
233-
case TopLevelConstructorExportDef(fullName, args, body) =>
234-
traverse(body)
235-
236233
case _:TopLevelJSClassExportDef | _:TopLevelModuleExportDef |
237234
_:TopLevelFieldExportDef =>
238235

ir/src/main/scala/org/scalajs/ir/Trees.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -985,9 +985,8 @@ object Trees {
985985

986986
sealed abstract class TopLevelExportDef extends IRNode {
987987
final def topLevelExportName: String = this match {
988-
case TopLevelConstructorExportDef(name, _, _) => name
989-
case TopLevelModuleExportDef(name) => name
990-
case TopLevelJSClassExportDef(name) => name
988+
case TopLevelModuleExportDef(name) => name
989+
case TopLevelJSClassExportDef(name) => name
991990

992991
case TopLevelMethodExportDef(MethodDef(_, propName, _, _, _)) =>
993992
val StringLiteral(name) = propName
@@ -997,9 +996,6 @@ object Trees {
997996
}
998997
}
999998

1000-
case class TopLevelConstructorExportDef(name: String, args: List[ParamDef],
1001-
body: Tree)(implicit val pos: Position) extends TopLevelExportDef
1002-
1003999
case class TopLevelJSClassExportDef(fullName: String)(
10041000
implicit val pos: Position) extends TopLevelExportDef
10051001

ir/src/test/scala/org/scalajs/ir/PrintersTest.scala

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,18 +1183,6 @@ class PrintersTest {
11831183
}
11841184
}
11851185

1186-
@Test def printConstructorExportDef(): Unit = {
1187-
assertPrintEquals(
1188-
"""
1189-
|export top constructor "pkg.Foo"(x: any) {
1190-
| 5
1191-
|}
1192-
""",
1193-
TopLevelConstructorExportDef("pkg.Foo",
1194-
List(ParamDef("x", AnyType, mutable = false, rest = false)),
1195-
i(5)))
1196-
}
1197-
11981186
@Test def printJSClassExportDef(): Unit = {
11991187
assertPrintEquals(
12001188
"""export top class "pkg.Foo"""",

linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -351,24 +351,20 @@ object Infos {
351351
def generateTopLevelExportsInfo(enclosingClass: String,
352352
topLevelExportDefs: List[TopLevelExportDef]): Option[MethodInfo] = {
353353

354-
var exportedConstructors: List[TopLevelConstructorExportDef] = Nil
355354
var topLevelMethodExports: List[TopLevelMethodExportDef] = Nil
356355
var topLevelFieldExports: List[TopLevelFieldExportDef] = Nil
357356

358357
topLevelExportDefs.foreach {
359-
case constructorDef: TopLevelConstructorExportDef =>
360-
exportedConstructors ::= constructorDef
361358
case _:TopLevelJSClassExportDef | _:TopLevelModuleExportDef =>
362359
case topLevelMethodExport: TopLevelMethodExportDef =>
363360
topLevelMethodExports ::= topLevelMethodExport
364361
case topLevelFieldExport: TopLevelFieldExportDef =>
365362
topLevelFieldExports ::= topLevelFieldExport
366363
}
367364

368-
if (exportedConstructors.nonEmpty || topLevelMethodExports.nonEmpty ||
369-
topLevelFieldExports.nonEmpty) {
365+
if (topLevelMethodExports.nonEmpty || topLevelFieldExports.nonEmpty) {
370366
Some(new GenInfoTraverser().generateTopLevelExportsInfo(enclosingClass,
371-
exportedConstructors, topLevelMethodExports, topLevelFieldExports))
367+
topLevelMethodExports, topLevelFieldExports))
372368
} else {
373369
None
374370
}
@@ -416,16 +412,12 @@ object Infos {
416412
}
417413

418414
def generateTopLevelExportsInfo(enclosingClass: String,
419-
topLevelConstructorDefs: List[TopLevelConstructorExportDef],
420415
topLevelMethodExports: List[TopLevelMethodExportDef],
421416
topLevelFieldExports: List[TopLevelFieldExportDef]): MethodInfo = {
422417
builder
423418
.setEncodedName(TopLevelExportsName)
424419
.setIsExported(true)
425420

426-
for (topLevelConstructorDef <- topLevelConstructorDefs)
427-
traverse(topLevelConstructorDef.body)
428-
429421
for (topLevelMethodExport <- topLevelMethodExports)
430422
topLevelMethodExport.methodDef.body.foreach(traverse(_))
431423

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,8 +1047,6 @@ private[emitter] final class ClassEmitter(jsGen: JSGen) {
10471047
implicit globalKnowledge: GlobalKnowledge): WithGlobals[List[js.Tree]] = {
10481048
val exportsWithGlobals = tree.topLevelExports.map { topLevelExport =>
10491049
topLevelExport.value match {
1050-
case e: TopLevelConstructorExportDef =>
1051-
genTopLevelConstructorExportDef(tree, e)
10521050
case e: TopLevelJSClassExportDef =>
10531051
WithGlobals(genTopLevelJSClassExportDef(tree, e))
10541052
case e: TopLevelModuleExportDef =>
@@ -1063,41 +1061,6 @@ private[emitter] final class ClassEmitter(jsGen: JSGen) {
10631061
WithGlobals.list(exportsWithGlobals)
10641062
}
10651063

1066-
def genTopLevelConstructorExportDef(cd: LinkedClass,
1067-
tree: TopLevelConstructorExportDef)(
1068-
implicit globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = {
1069-
import TreeDSL._
1070-
1071-
implicit val pos = tree.pos
1072-
val classType = ClassType(cd.name.name)
1073-
val TopLevelConstructorExportDef(fullName, args, body) = tree
1074-
1075-
val baseCtor = envField("c", cd.name.name, cd.name.originalName)
1076-
1077-
val generatedFunWithGlobals = desugarToFunctionWithExplicitThis(
1078-
cd.encodedName, args, body, resultType = NoType)
1079-
1080-
for (generatedFun <- generatedFunWithGlobals) yield {
1081-
val js.Function(arrow, thisParam :: ctorParams, ctorBody) = generatedFun
1082-
val thisIdent = thisParam.name
1083-
1084-
val exportedCtor = js.Function(arrow, ctorParams, js.Block(
1085-
genLet(thisIdent, mutable = false, js.New(baseCtor, Nil)),
1086-
ctorBody,
1087-
js.Return(js.VarRef(thisIdent))
1088-
))
1089-
1090-
val (createNamespace, expCtorVar) =
1091-
genCreateNamespaceInExports(fullName)
1092-
js.Block(
1093-
createNamespace,
1094-
js.DocComment("@constructor"),
1095-
expCtorVar := exportedCtor,
1096-
expCtorVar DOT "prototype" := baseCtor DOT "prototype"
1097-
)
1098-
}
1099-
}
1100-
11011064
def genTopLevelJSClassExportDef(cd: LinkedClass,
11021065
tree: TopLevelJSClassExportDef): js.Tree = {
11031066
import TreeDSL._

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/KnowledgeGuardian.scala

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,19 +109,14 @@ private[emitter] final class KnowledgeGuardian {
109109
/* We can enable inlined init if all of the following apply:
110110
* - The class is not blacklisted
111111
* - It does not have any instantiated subclass
112-
* - It has exactly one (regular) constructor
113-
* - It does not have any exported constructor (since they are
114-
* effectively secondary constructors)
112+
* - It has exactly one constructor
115113
*
116114
* By construction, this is always true for module classes.
117115
*/
118116
!blackList(classDef.encodedName) &&
119117
!classesWithInstantiatedSubclasses(classDef.encodedName) && {
120118
classDef.memberMethods.count(
121119
x => Definitions.isConstructorName(x.value.encodedName)) == 1
122-
} && {
123-
!classDef.topLevelExports.exists(
124-
_.value.isInstanceOf[TopLevelConstructorExportDef])
125120
}
126121
}
127122

linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,6 @@ private final class IRChecker(unit: LinkingUnit,
219219
implicit val ctx = ErrorContext(tree)
220220

221221
tree match {
222-
case tree: TopLevelConstructorExportDef =>
223-
checkTopLevelConstructorExportDef(tree, classDef)
224-
225222
case tree: TopLevelJSClassExportDef =>
226223
checkTopLevelJSClassExportDef(tree, classDef)
227224

@@ -492,24 +489,6 @@ private final class IRChecker(unit: LinkingUnit,
492489
}
493490
}
494491

495-
private def checkTopLevelConstructorExportDef(
496-
ctorDef: TopLevelConstructorExportDef,
497-
classDef: LinkedClass): Unit = withPerMethodState {
498-
val TopLevelConstructorExportDef(_, params, body) = ctorDef
499-
implicit val ctx = ErrorContext(ctorDef)
500-
501-
if (!classDef.kind.isClass) {
502-
reportError(s"Exported constructor def can only appear in a class")
503-
return
504-
}
505-
506-
checkJSParamDefs(params)
507-
508-
val thisType = ClassType(classDef.name.name)
509-
val bodyEnv = Env.fromSignature(thisType, None, params, NoType)
510-
typecheckStat(body, bodyEnv)
511-
}
512-
513492
private def checkTopLevelJSClassExportDef(
514493
classExportDef: TopLevelJSClassExportDef, classDef: LinkedClass): Unit = {
515494
implicit val ctx = ErrorContext(classExportDef)

0 commit comments

Comments
 (0)
0