Skip to content

Commit b694955

Browse files
authored
Merge pull request #4931 from sjrd/minify-prototype
More minification.
2 parents 8dd02c7 + 0dd63ff commit b694955

File tree

11 files changed

+501
-121
lines changed

11 files changed

+501
-121
lines changed

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

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,13 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
186186

187187
val chainProtoWithGlobals = superClass match {
188188
case None =>
189-
WithGlobals.nil
189+
WithGlobals(setPrototypeVar(ctorVar))
190190

191191
case Some(_) if shouldExtendJSError(className, superClass) =>
192-
globalRef("Error").map(chainPrototypeWithLocalCtor(className, ctorVar, _))
192+
globalRef("Error").map(chainPrototypeWithLocalCtor(className, ctorVar, _, localDeclPrototypeVar = false))
193193

194194
case Some(parentIdent) =>
195-
WithGlobals(List(ctorVar.prototype := js.New(globalVar(VarField.h, parentIdent.name), Nil)))
195+
WithGlobals(List(genAssignPrototype(ctorVar, js.New(globalVar(VarField.h, parentIdent.name), Nil))))
196196
}
197197

198198
for {
@@ -208,12 +208,12 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
208208
js.JSDocConstructor(realCtorDef.head) ::
209209
realCtorDef.tail :::
210210
chainProto :::
211-
(genIdentBracketSelect(ctorVar.prototype, "constructor") := ctorVar) ::
211+
(genIdentBracketSelect(prototypeFor(ctorVar), "constructor") := ctorVar) ::
212212

213213
// Inheritable constructor
214214
js.JSDocConstructor(inheritableCtorDef.head) ::
215215
inheritableCtorDef.tail :::
216-
(globalVar(VarField.h, className).prototype := ctorVar.prototype) :: Nil
216+
(globalVar(VarField.h, className).prototype := prototypeFor(ctorVar)) :: Nil
217217
)
218218
}
219219
}
@@ -243,8 +243,8 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
243243
val ctorVar = fileLevelVar(VarField.b, genName(className))
244244

245245
js.JSDocConstructor(ctorVar := ctorFun) ::
246-
chainPrototypeWithLocalCtor(className, ctorVar, superCtor) :::
247-
(genIdentBracketSelect(ctorVar.prototype, "constructor") := ctorVar) :: Nil
246+
chainPrototypeWithLocalCtor(className, ctorVar, superCtor, localDeclPrototypeVar = true) :::
247+
(genIdentBracketSelect(prototypeFor(ctorVar), "constructor") := ctorVar) :: Nil
248248
}
249249
}
250250
}
@@ -333,15 +333,15 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
333333
}
334334

335335
private def chainPrototypeWithLocalCtor(className: ClassName, ctorVar: js.Tree,
336-
superCtor: js.Tree)(implicit pos: Position): List[js.Tree] = {
336+
superCtor: js.Tree, localDeclPrototypeVar: Boolean)(implicit pos: Position): List[js.Tree] = {
337337
import TreeDSL._
338338

339339
val dummyCtor = fileLevelVar(VarField.hh, genName(className))
340340

341341
List(
342342
js.JSDocConstructor(genConst(dummyCtor.ident, js.Function(false, Nil, None, js.Skip()))),
343343
dummyCtor.prototype := superCtor.prototype,
344-
ctorVar.prototype := js.New(dummyCtor, Nil)
344+
genAssignPrototype(ctorVar, js.New(dummyCtor, Nil), localDeclPrototypeVar)
345345
)
346346
}
347347

@@ -638,7 +638,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
638638
else globalVar(VarField.c, className)
639639

640640
if (namespace.isStatic) classVarRef
641-
else classVarRef.prototype
641+
else prototypeFor(classVarRef)
642642
}
643643

644644
def genMemberNameTree(name: Tree)(
@@ -826,7 +826,7 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
826826

827827
def genTypeData(className: ClassName, kind: ClassKind,
828828
superClass: Option[ClassIdent], ancestors: List[ClassName],
829-
jsNativeLoadSpec: Option[JSNativeLoadSpec])(
829+
jsNativeLoadSpec: Option[JSNativeLoadSpec], hasInstances: Boolean)(
830830
implicit moduleContext: ModuleContext,
831831
globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[List[js.Tree]] = {
832832
import TreeDSL._
@@ -836,21 +836,35 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
836836
val isJSType =
837837
kind.isJSType
838838

839-
val isJSTypeParam =
840-
if (isJSType) js.BooleanLiteral(true)
841-
else js.Undefined()
839+
/* The `kindOrCtor` param is either:
840+
* - an int: 1 means isInterface; 2 means isJSType; 0 otherwise
841+
* - a Scala class constructor: means 0 + assign `kindOrCtor.prototype.$classData = <this TypeData>;`
842+
*
843+
* We must only assign the `$classData` if the class is a regular
844+
* (non-hijacked) Scala class, and if it has instances. Otherwise there is
845+
* no Scala class constructor for the class at all.
846+
*/
847+
val kindOrCtorParam = {
848+
if (isJSType) js.IntLiteral(2)
849+
else if (kind == ClassKind.Interface) js.IntLiteral(1)
850+
else if (kind.isClass && hasInstances) globalVar(VarField.c, className)
851+
else js.IntLiteral(0)
852+
}
842853

843-
val parentData = if (globalKnowledge.isParentDataAccessed) {
844-
superClass.fold[js.Tree] {
854+
val parentDataOpt = if (globalKnowledge.isParentDataAccessed) {
855+
val parentData = superClass.fold[js.Tree] {
845856
if (isObjectClass) js.Null()
846857
else js.Undefined()
847858
} { parent =>
848859
globalVar(VarField.d, parent.name)
849860
}
861+
parentData :: Nil
850862
} else {
851-
js.Undefined()
863+
Nil
852864
}
853865

866+
assert(ancestors.headOption.contains(className),
867+
s"The ancestors of ${className.nameString} do not start with itself: $ancestors")
854868
val ancestorsRecord = js.ObjectConstr(
855869
ancestors.withFilter(_ != ObjectClass).map(ancestor => (genAncestorIdent(ancestor), js.IntLiteral(1)))
856870
)
@@ -902,15 +916,11 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
902916

903917
isInstanceFunWithGlobals.flatMap { isInstanceFun =>
904918
val allParams = List(
905-
js.ObjectConstr(List(genAncestorIdent(className) -> js.IntLiteral(0))),
906-
js.BooleanLiteral(kind == ClassKind.Interface),
919+
kindOrCtorParam,
907920
js.StringLiteral(RuntimeClassNameMapperImpl.map(
908921
semantics.runtimeClassNameMapper, className.nameString)),
909-
ancestorsRecord,
910-
isJSTypeParam,
911-
parentData,
912-
isInstanceFun
913-
)
922+
ancestorsRecord
923+
) ::: parentDataOpt ::: isInstanceFun :: Nil
914924

915925
val prunedParams =
916926
allParams.reverse.dropWhile(_.isInstanceOf[js.Undefined]).reverse
@@ -922,14 +932,6 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
922932
}
923933
}
924934

925-
def genSetTypeData(className: ClassName)(
926-
implicit moduleContext: ModuleContext,
927-
globalKnowledge: GlobalKnowledge, pos: Position): js.Tree = {
928-
import TreeDSL._
929-
930-
globalVar(VarField.c, className).prototype DOT cpn.classData := globalVar(VarField.d, className)
931-
}
932-
933935
def genModuleAccessor(className: ClassName, isJSClass: Boolean)(
934936
implicit moduleContext: ModuleContext,
935937
globalKnowledge: GlobalKnowledge, pos: Position): WithGlobals[List[js.Tree]] = {

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

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ private[emitter] object CoreJSLib {
130130
defineLinkingInfo() :::
131131
defineJSBuiltinsSnapshotsAndPolyfills() :::
132132
declareCachedL0() :::
133-
definePropertyName() :::
134133
defineCharClass() :::
135134
defineRuntimeFunctions() :::
136135
defineObjectGetClassFunctions() :::
@@ -526,23 +525,6 @@ private[emitter] object CoreJSLib {
526525
))
527526
}
528527

529-
private def definePropertyName(): List[Tree] = {
530-
/* Encodes a property name for runtime manipulation.
531-
*
532-
* Usage:
533-
* env.propertyName({someProp:0})
534-
* Returns:
535-
* "someProp"
536-
* Useful when the property is renamed by a global optimizer (like
537-
* Closure) but we must still get hold of a string of that name for
538-
* runtime reflection.
539-
*/
540-
defineFunction1(VarField.propertyName) { obj =>
541-
val prop = varRef("prop")
542-
ForIn(genEmptyImmutableLet(prop.ident), obj, Return(prop))
543-
}
544-
}
545-
546528
private def defineCharClass(): List[Tree] = {
547529
val ctor = {
548530
val c = varRef("c")
@@ -562,6 +544,7 @@ private[emitter] object CoreJSLib {
562544
extractWithGlobals(globalClassDef(VarField.Char, CoreVar, None, ctor :: toStr :: Nil))
563545
} else {
564546
defineFunction(VarField.Char, ctor.args, ctor.body) :::
547+
setPrototypeVar(globalVar(VarField.Char, CoreVar)) :::
565548
assignES5ClassMembers(globalVar(VarField.Char, CoreVar), List(toStr))
566549
}
567550
}
@@ -1528,16 +1511,16 @@ private[emitter] object CoreJSLib {
15281511
val clsDef = {
15291512
extractWithGlobals(globalFunctionDef(VarField.ac, componentTypeRef,
15301513
ctor.args, ctor.restParam, ctor.body)) :::
1531-
(ArrayClass.prototype := New(globalVar(VarField.h, ObjectClass), Nil)) ::
1532-
(ArrayClass.prototype DOT "constructor" := ArrayClass) ::
1514+
genAssignPrototype(ArrayClass, New(globalVar(VarField.h, ObjectClass), Nil)) ::
1515+
(prototypeFor(ArrayClass) DOT "constructor" := ArrayClass) ::
15331516
assignES5ClassMembers(ArrayClass, members)
15341517
}
15351518

15361519
componentTypeRef match {
15371520
case _: ClassRef =>
15381521
clsDef :::
15391522
extractWithGlobals(globalFunctionDef(VarField.ah, ObjectClass, Nil, None, Skip())) :::
1540-
(globalVar(VarField.ah, ObjectClass).prototype := ArrayClass.prototype) :: Nil
1523+
(globalVar(VarField.ah, ObjectClass).prototype := prototypeFor(ArrayClass)) :: Nil
15411524
case _: PrimRef =>
15421525
clsDef
15431526
}
@@ -1651,23 +1634,34 @@ private[emitter] object CoreJSLib {
16511634
}
16521635

16531636
val initClass = {
1654-
val internalNameObj = varRef("internalNameObj")
1655-
val isInterface = varRef("isInterface")
1637+
/* This is either:
1638+
* - an int: 1 means isInterface; 2 means isJSType; 0 otherwise
1639+
* - a Scala class constructor: means 0 + assign `kindOrCtor.prototype.$classData = this;`
1640+
*/
1641+
val kindOrCtor = varRef("kindOrCtor")
1642+
1643+
val hasParentData = globalKnowledge.isParentDataAccessed
1644+
16561645
val fullName = varRef("fullName")
16571646
val ancestors = varRef("ancestors")
1658-
val isJSType = varRef("isJSType")
16591647
val parentData = varRef("parentData")
16601648
val isInstance = varRef("isInstance")
16611649
val internalName = varRef("internalName")
16621650
val that = varRef("that")
16631651
val depth = varRef("depth")
16641652
val obj = varRef("obj")
1665-
MethodDef(static = false, Ident(cpn.initClass),
1666-
paramList(internalNameObj, isInterface, fullName, ancestors,
1667-
isJSType, parentData, isInstance), None, {
1653+
val params =
1654+
if (hasParentData) paramList(kindOrCtor, fullName, ancestors, parentData, isInstance)
1655+
else paramList(kindOrCtor, fullName, ancestors, isInstance)
1656+
MethodDef(static = false, Ident(cpn.initClass), params, None, {
16681657
Block(
1669-
const(internalName, genCallHelper(VarField.propertyName, internalNameObj)),
1670-
if (globalKnowledge.isParentDataAccessed)
1658+
/* Extract the internalName, which is the first property of ancestors.
1659+
* We use `getOwnPropertyNames()`, which since ES 2015 guarantees
1660+
* to return non-integer string keys in creation order.
1661+
*/
1662+
const(internalName,
1663+
BracketSelect(Apply(genIdentBracketSelect(ObjectRef, "getOwnPropertyNames"), List(ancestors)), 0)),
1664+
if (hasParentData)
16711665
privateFieldSet(cpn.parentData, parentData)
16721666
else
16731667
Skip(),
@@ -1678,15 +1672,18 @@ private[emitter] object CoreJSLib {
16781672
Return(!(!(BracketSelect(that DOT cpn.ancestors, internalName))))
16791673
})
16801674
}),
1681-
privateFieldSet(cpn.isJSType, !(!isJSType)),
1675+
privateFieldSet(cpn.isJSType, kindOrCtor === 2),
16821676
publicFieldSet(cpn.name, fullName),
1683-
publicFieldSet(cpn.isInterface, isInterface),
1677+
publicFieldSet(cpn.isInterface, kindOrCtor === 1),
16841678
publicFieldSet(cpn.isInstance, isInstance || {
16851679
genArrowFunction(paramList(obj), {
16861680
Return(!(!(obj && (obj DOT classData) &&
16871681
BracketSelect(obj DOT classData DOT cpn.ancestors, internalName))))
16881682
})
16891683
}),
1684+
If(typeof(kindOrCtor) !== str("number"), {
1685+
kindOrCtor.prototype DOT cpn.classData := This()
1686+
}),
16901687
Return(This())
16911688
)
16921689
})
@@ -1697,7 +1694,6 @@ private[emitter] object CoreJSLib {
16971694
val name = varRef("name")
16981695

16991696
Block(
1700-
arrayClass.prototype DOT classData := This(),
17011697
const(name, str("[") + (componentData DOT cpn.arrayEncodedName)),
17021698
privateFieldSet(cpn.constr, arrayClass),
17031699
if (globalKnowledge.isParentDataAccessed)
@@ -1729,6 +1725,7 @@ private[emitter] object CoreJSLib {
17291725
MethodDef(static = false, Ident(cpn.initSpecializedArray),
17301726
paramList(componentData, arrayClass, typedArrayClass, isAssignableFromFun), None, {
17311727
Block(
1728+
arrayClass.prototype DOT classData := This(),
17321729
initArrayCommonBody(arrayClass, componentData, componentData, 1),
17331730
const(self, This()), // capture `this` for use in arrow fun
17341731
privateFieldSet(cpn.isAssignableFromFun, isAssignableFromFun || {
@@ -1833,14 +1830,19 @@ private[emitter] object CoreJSLib {
18331830
val members = set ::: copyTo ::: clone :: Nil
18341831

18351832
if (useClassesForRegularClasses) {
1836-
ClassDef(Some(ArrayClass.ident), Some(globalVar(VarField.ac, ObjectClass)),
1837-
ctor :: members)
1833+
Block(
1834+
ClassDef(Some(ArrayClass.ident), Some(globalVar(VarField.ac, ObjectClass)),
1835+
ctor :: members),
1836+
ArrayClass.prototype DOT cpn.classData := This()
1837+
)
18381838
} else {
18391839
Block(
18401840
FunctionDef(ArrayClass.ident, ctor.args, ctor.restParam, ctor.body) ::
1841-
(ArrayClass.prototype := New(globalVar(VarField.ah, ObjectClass), Nil)) ::
1842-
(ArrayClass.prototype DOT "constructor" := ArrayClass) ::
1843-
assignES5ClassMembers(ArrayClass, members)
1841+
genAssignPrototype(ArrayClass, New(globalVar(VarField.ah, ObjectClass), Nil), localDecl = true) ::
1842+
(prototypeFor(ArrayClass) DOT "constructor" := ArrayClass) ::
1843+
assignES5ClassMembers(ArrayClass, members) :::
1844+
(prototypeFor(ArrayClass) DOT cpn.classData := This()) ::
1845+
Nil
18441846
)
18451847
}
18461848
}
@@ -2000,6 +2002,7 @@ private[emitter] object CoreJSLib {
20002002
extractWithGlobals(globalClassDef(VarField.TypeData, CoreVar, None, ctor :: members))
20012003
} else {
20022004
defineFunction(VarField.TypeData, ctor.args, ctor.body) :::
2005+
setPrototypeVar(globalVar(VarField.TypeData, CoreVar)) :::
20032006
assignES5ClassMembers(globalVar(VarField.TypeData, CoreVar), members)
20042007
}
20052008
}
@@ -2159,7 +2162,7 @@ private[emitter] object CoreJSLib {
21592162
for {
21602163
MethodDef(static, name, args, restParam, body) <- members
21612164
} yield {
2162-
val target = if (static) classRef else classRef.prototype
2165+
val target = if (static) classRef else prototypeFor(classRef)
21632166
genPropSelect(target, name) := Function(arrow = false, args, restParam, body)
21642167
}
21652168
}

0 commit comments

Comments
 (0)