Skip to content

Commit 7c6659a

Browse files
committed
Refactor constructor generation
We split on the class kind (Scala / JS) first and then on the output class style (ES5 / ES6).
1 parent 003a60f commit 7c6659a

File tree

2 files changed

+83
-91
lines changed

2 files changed

+83
-91
lines changed

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

Lines changed: 53 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -169,57 +169,51 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
169169
}
170170
}
171171

172-
/** Generates the JS constructor for a class. */
173-
def genConstructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent],
174-
jsSuperClass: Option[Tree], useESClass: Boolean,
175-
initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])(
172+
/** Generates the JS constructor for a Scala class. */
173+
def genScalaClassConstructor(className: ClassName, superClass: Option[ClassIdent],
174+
useESClass: Boolean, initToInline: Option[MethodDef])(
176175
implicit moduleContext: ModuleContext,
177176
globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = {
178177

179-
assert(kind.isAnyNonNativeClass)
180178
assert(superClass.isDefined || className == ObjectClass,
181179
s"Class $className is missing a parent class")
182180

183-
if (useESClass)
184-
genES6Constructor(className, kind, superClass, initToInline, jsConstructorDef)
185-
else
186-
genES5Constructor(className, kind, superClass, jsSuperClass, initToInline, jsConstructorDef)
187-
}
181+
val jsConstructorFunWithGlobals =
182+
genJSConstructorFun(className, superClass, initToInline, useESClass)
188183

189-
/** Generates the JS constructor for a class, ES5 style. */
190-
private def genES5Constructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent],
191-
jsSuperClass: Option[Tree], initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])(
192-
implicit moduleContext: ModuleContext,
193-
globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = {
194-
import TreeDSL._
184+
if (useESClass) {
185+
for (jsConstructorFun <- jsConstructorFunWithGlobals) yield {
186+
val js.Function(_, args, restParam, body) = jsConstructorFun
195187

196-
def chainPrototypeWithLocalCtor(ctorVar: js.Tree, superCtor: js.Tree): js.Tree = {
197-
val dummyCtor = fileLevelVar("hh", genName(className))
188+
def isTrivialCtorBody: Boolean = body match {
189+
case js.Skip() => true
190+
case js.Apply(js.Super(), Nil) => true
191+
case _ => false
192+
}
198193

199-
js.Block(
200-
js.DocComment("@constructor"),
201-
genConst(dummyCtor.ident, js.Function(false, Nil, None, js.Skip())),
202-
dummyCtor.prototype := superCtor.prototype,
203-
ctorVar.prototype := js.New(dummyCtor, Nil)
204-
)
205-
}
194+
if (args.isEmpty && isTrivialCtorBody)
195+
js.Skip()
196+
else
197+
js.MethodDef(static = false, js.Ident("constructor"), args, restParam, body)
198+
}
199+
} else {
200+
import TreeDSL._
206201

207-
if (!kind.isJSClass) {
208202
val ctorVar = globalVar("c", className)
209203

210204
val chainProtoWithGlobals = superClass match {
211205
case None =>
212206
WithGlobals(js.Skip())
213207

214208
case Some(_) if shouldExtendJSError(className, superClass) =>
215-
untrackedGlobalRef("Error").map(chainPrototypeWithLocalCtor(ctorVar, _))
209+
untrackedGlobalRef("Error").map(chainPrototypeWithLocalCtor(className, ctorVar, _))
216210

217211
case Some(parentIdent) =>
218212
WithGlobals(ctorVar.prototype := js.New(globalVar("h", parentIdent.name), Nil))
219213
}
220214

221215
for {
222-
ctorFun <- genJSConstructorFun(className, superClass, initToInline, forESClass = false)
216+
ctorFun <- jsConstructorFunWithGlobals
223217
realCtorDef <-
224218
globalFunctionDef("c", className, ctorFun.args, ctorFun.restParam, ctorFun.body)
225219
inheritableCtorDef <-
@@ -239,50 +233,38 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
239233
globalVar("h", className).prototype := ctorVar.prototype
240234
)
241235
}
242-
} else {
243-
for {
244-
ctorFun <- genConstructorFunForJSClass(className, jsConstructorDef)
245-
superCtor <- genJSSuperCtor(superClass, jsSuperClass)
246-
} yield {
247-
val ctorVar = fileLevelVar("b", genName(className))
248-
249-
js.Block(
250-
js.DocComment("@constructor"),
251-
ctorVar := ctorFun,
252-
chainPrototypeWithLocalCtor(ctorVar, superCtor),
253-
genIdentBracketSelect(ctorVar.prototype, "constructor") := ctorVar
254-
)
255-
}
256236
}
257237
}
258238

259-
/** Generates the JS constructor for a class, ES6 style. */
260-
private def genES6Constructor(className: ClassName, kind: ClassKind, superClass: Option[ClassIdent],
261-
initToInline: Option[MethodDef], jsConstructorDef: Option[JSConstructorDef])(
239+
/** Generates the JS constructor for a JS class. */
240+
def genJSConstructor(className: ClassName, superClass: Option[ClassIdent],
241+
jsSuperClass: Option[Tree], useESClass: Boolean, jsConstructorDef: JSConstructorDef)(
262242
implicit moduleContext: ModuleContext,
263243
globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Tree] = {
264-
if (kind.isJSClass) {
265-
for (fun <- genConstructorFunForJSClass(className, jsConstructorDef)) yield {
244+
245+
val JSConstructorDef(_, params, restParam, body) = jsConstructorDef
246+
val ctorFunWithGlobals = desugarToFunction(className, params, restParam, body)
247+
248+
if (useESClass) {
249+
for (fun <- ctorFunWithGlobals) yield {
266250
js.MethodDef(static = false, js.Ident("constructor"),
267251
fun.args, fun.restParam, fun.body)
268252
}
269253
} else {
270-
val jsConstructorFunWithGlobals =
271-
genJSConstructorFun(className, superClass, initToInline, forESClass = true)
272-
273-
for (jsConstructorFun <- jsConstructorFunWithGlobals) yield {
274-
val js.Function(_, args, restParam, body) = jsConstructorFun
254+
for {
255+
ctorFun <- ctorFunWithGlobals
256+
superCtor <- genJSSuperCtor(superClass, jsSuperClass)
257+
} yield {
258+
import TreeDSL._
275259

276-
def isTrivialCtorBody: Boolean = body match {
277-
case js.Skip() => true
278-
case js.Apply(js.Super(), Nil) => true
279-
case _ => false
280-
}
260+
val ctorVar = fileLevelVar("b", genName(className))
281261

282-
if (args.isEmpty && isTrivialCtorBody)
283-
js.Skip()
284-
else
285-
js.MethodDef(static = false, js.Ident("constructor"), args, restParam, body)
262+
js.Block(
263+
js.DocComment("@constructor"),
264+
ctorVar := ctorFun,
265+
chainPrototypeWithLocalCtor(className, ctorVar, superCtor),
266+
genIdentBracketSelect(ctorVar.prototype, "constructor") := ctorVar
267+
)
286268
}
287269
}
288270
}
@@ -365,17 +347,18 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
365347
}
366348
}
367349

368-
private def genConstructorFunForJSClass(className: ClassName,
369-
jsConstructorDef: Option[JSConstructorDef])(
370-
implicit moduleContext: ModuleContext,
371-
globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[js.Function] = {
350+
private def chainPrototypeWithLocalCtor(className: ClassName, ctorVar: js.Tree,
351+
superCtor: js.Tree)(implicit pos: Position): js.Tree = {
352+
import TreeDSL._
372353

373-
val JSConstructorDef(_, params, restParam, body) = jsConstructorDef.getOrElse {
374-
throw new IllegalArgumentException(
375-
s"$className does not have an exported constructor")
376-
}
354+
val dummyCtor = fileLevelVar("hh", genName(className))
377355

378-
desugarToFunction(className, params, restParam, body)
356+
js.Block(
357+
js.DocComment("@constructor"),
358+
genConst(dummyCtor.ident, js.Function(false, Nil, None, js.Skip())),
359+
dummyCtor.prototype := superCtor.prototype,
360+
ctorVar.prototype := js.New(dummyCtor, Nil)
361+
)
379362
}
380363

381364
/** Generates the creation of fields for a Scala class. */

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

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -460,29 +460,38 @@ final class Emitter(config: Emitter.Config) {
460460
* If it is a JS class, it depends on the jsConstructorDef.
461461
*/
462462
val ctorCache = classCache.getConstructorCache()
463-
val ctorVersion = {
464-
if (linkedClass.kind.isJSClass) {
465-
assert(linkedInlineableInit.isEmpty)
466-
Version.combine(linkedClass.version, linkedClass.jsConstructorDef.get.version)
467-
} else {
468-
linkedInlineableInit.fold {
469-
Version.combine(linkedClass.version)
470-
} { linkedInit =>
471-
Version.combine(linkedClass.version, linkedInit.version)
472-
}
463+
464+
if (linkedClass.kind.isJSClass) {
465+
assert(linkedInlineableInit.isEmpty)
466+
467+
val jsConstructorDef = linkedClass.jsConstructorDef.getOrElse {
468+
throw new IllegalArgumentException(s"$className does not have an exported constructor")
469+
}
470+
471+
val ctorVersion = Version.combine(linkedClass.version, jsConstructorDef.version)
472+
ctorCache.getOrElseUpdate(ctorVersion,
473+
classEmitter.genJSConstructor(
474+
className, // invalidated by overall class cache (part of ancestors)
475+
linkedClass.superClass, // invalidated by class version
476+
linkedClass.jsSuperClass, // invalidated by class version
477+
useESClass, // invalidated by class version
478+
jsConstructorDef // part of ctor version
479+
)(moduleContext, ctorCache, linkedClass.pos))
480+
} else {
481+
val ctorVersion = linkedInlineableInit.fold {
482+
Version.combine(linkedClass.version)
483+
} { linkedInit =>
484+
Version.combine(linkedClass.version, linkedInit.version)
473485
}
486+
487+
ctorCache.getOrElseUpdate(ctorVersion,
488+
classEmitter.genScalaClassConstructor(
489+
className, // invalidated by overall class cache (part of ancestors)
490+
linkedClass.superClass, // invalidated by class version
491+
useESClass, // invalidated by class version,
492+
linkedInlineableInit // part of ctor version
493+
)(moduleContext, ctorCache, linkedClass.pos))
474494
}
475-
// TODO: This is probably better split into JS / Scala class ctors.
476-
ctorCache.getOrElseUpdate(ctorVersion,
477-
classEmitter.genConstructor(
478-
className, // invalidated by overall class cache (part of ancestors)
479-
kind, // invalidated by class verison
480-
linkedClass.superClass, // invalidated by class version
481-
linkedClass.jsSuperClass, // invalidated by class version
482-
useESClass, // invalidated by class version,
483-
linkedInlineableInit, // part of ctor version
484-
linkedClass.jsConstructorDef // part of ctor version
485-
)(moduleContext, ctorCache, linkedClass.pos))
486495
}
487496

488497
/* Bridges from Throwable to methods of Object, which are necessary

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy