Skip to content

Commit 723663b

Browse files
committed
Refactor: Make FieldName a composite of ClassName and SimpleFieldName.
Previously, `FieldName` only represented the *simple* name of a field. It was complemented everywhere with the enclosing `ClassName`, for namespacing purposes. We now make `FieldName` a composite, like `MethodName`. It contains a `ClassName` and a `SimpleFieldName`. Structurally, `SimpleFieldName` is the same as the old `FieldName`. This removes the need to pass additional, out-of-band `ClassName`s everywhere a `FieldName` or `FieldIdent` was used. In addition to the readability improvements, this might improve performance. We previously often had to create (temporary) pairs of `(ClassName, FieldName)` as keys of maps. Now, we can directly use the `FieldName`s instead. While the IR names, types and trees are significantly impacted by this change, the `.sjsir` format is unchanged.
1 parent b1daaa7 commit 723663b

32 files changed

+479
-357
lines changed

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import scala.reflect.internal.Flags
2727

2828
import org.scalajs.ir
2929
import org.scalajs.ir.{Trees => js, Types => jstpe, ClassKind, Hashers, OriginalName}
30-
import org.scalajs.ir.Names.{LocalName, FieldName, SimpleMethodName, MethodName, ClassName}
30+
import org.scalajs.ir.Names.{LocalName, SimpleFieldName, FieldName, SimpleMethodName, MethodName, ClassName}
3131
import org.scalajs.ir.OriginalName.NoOriginalName
3232
import org.scalajs.ir.Trees.OptimizerHints
3333
import org.scalajs.ir.Version.Unversioned
@@ -6337,7 +6337,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
63376337
val classType = jstpe.ClassType(className)
63386338

63396339
// val f: Any
6340-
val fFieldIdent = js.FieldIdent(FieldName("f"))
6340+
val fFieldIdent = js.FieldIdent(FieldName(className, SimpleFieldName("f")))
63416341
val fFieldDef = js.FieldDef(js.MemberFlags.empty, fFieldIdent,
63426342
NoOriginalName, jstpe.AnyType)
63436343

@@ -6353,8 +6353,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
63536353
jstpe.NoType,
63546354
Some(js.Block(List(
63556355
js.Assign(
6356-
js.Select(js.This()(classType), className, fFieldIdent)(
6357-
jstpe.AnyType),
6356+
js.Select(js.This()(classType), fFieldIdent)(jstpe.AnyType),
63586357
fParamDef.ref),
63596358
js.ApplyStatically(js.ApplyFlags.empty.withConstructor(true),
63606359
js.This()(classType),
@@ -6405,7 +6404,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
64056404
}.map((ensureBoxed _).tupled)
64066405

64076406
val call = js.JSFunctionApply(
6408-
js.Select(js.This()(classType), className, fFieldIdent)(jstpe.AnyType),
6407+
js.Select(js.This()(classType), fFieldIdent)(jstpe.AnyType),
64096408
actualParams)
64106409

64116410
val body = fromAny(call, enteringPhase(currentRun.posterasurePhase) {
@@ -6746,14 +6745,12 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
67466745
js.JSSelect(qual, genPrivateFieldsSymbol()),
67476746
encodeFieldSymAsStringLiteral(sym))
67486747
} else {
6749-
js.JSPrivateSelect(qual, encodeClassName(sym.owner),
6750-
encodeFieldSym(sym))
6748+
js.JSPrivateSelect(qual, encodeFieldSym(sym))
67516749
}
67526750

67536751
(f, true)
67546752
} else if (jsInterop.topLevelExportsOf(sym).nonEmpty) {
6755-
val f = js.SelectStatic(encodeClassName(sym.owner),
6756-
encodeFieldSym(sym))(jstpe.AnyType)
6753+
val f = js.SelectStatic(encodeFieldSym(sym))(jstpe.AnyType)
67576754
(f, true)
67586755
} else if (jsInterop.staticExportsOf(sym).nonEmpty) {
67596756
val exportInfo = jsInterop.staticExportsOf(sym).head
@@ -6764,7 +6761,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
67646761

67656762
(f, true)
67666763
} else {
6767-
val className = encodeClassName(sym.owner)
67686764
val fieldIdent = encodeFieldSym(sym)
67696765

67706766
/* #4370 Fields cannot have type NothingType, so we box them as
@@ -6774,11 +6770,11 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
67746770
*/
67756771
toIRType(sym.tpe) match {
67766772
case jstpe.NothingType =>
6777-
val f = js.Select(qual, className, fieldIdent)(
6773+
val f = js.Select(qual, fieldIdent)(
67786774
encodeClassType(RuntimeNothingClass))
67796775
(f, true)
67806776
case ftpe =>
6781-
val f = js.Select(qual, className, fieldIdent)(ftpe)
6777+
val f = js.Select(qual, fieldIdent)(ftpe)
67826778
(f, false)
67836779
}
67846780
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import scala.tools.nsc._
1818

1919
import org.scalajs.ir
2020
import org.scalajs.ir.{Trees => js, Types => jstpe}
21-
import org.scalajs.ir.Names.{LocalName, LabelName, FieldName, SimpleMethodName, MethodName, ClassName}
21+
import org.scalajs.ir.Names.{LocalName, LabelName, SimpleFieldName, FieldName, SimpleMethodName, MethodName, ClassName}
2222
import org.scalajs.ir.OriginalName
2323
import org.scalajs.ir.OriginalName.NoOriginalName
2424
import org.scalajs.ir.UTF8String
@@ -178,8 +178,9 @@ trait JSEncoding[G <: Global with Singleton] extends SubComponent {
178178

179179
def encodeFieldSym(sym: Symbol)(implicit pos: Position): js.FieldIdent = {
180180
requireSymIsField(sym)
181-
val name = sym.name.dropLocal
182-
js.FieldIdent(FieldName(name.toString()))
181+
val className = encodeClassName(sym.owner)
182+
val simpleName = SimpleFieldName(sym.name.dropLocal.toString())
183+
js.FieldIdent(FieldName(className, simpleName))
183184
}
184185

185186
def encodeFieldSymAsStringLiteral(sym: Symbol)(

ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -262,16 +262,14 @@ object Hashers {
262262
case StoreModule() =>
263263
mixTag(TagStoreModule)
264264

265-
case Select(qualifier, className, field) =>
265+
case Select(qualifier, field) =>
266266
mixTag(TagSelect)
267267
mixTree(qualifier)
268-
mixName(className)
269268
mixFieldIdent(field)
270269
mixType(tree.tpe)
271270

272-
case SelectStatic(className, field) =>
271+
case SelectStatic(field) =>
273272
mixTag(TagSelectStatic)
274-
mixName(className)
275273
mixFieldIdent(field)
276274
mixType(tree.tpe)
277275

@@ -351,7 +349,7 @@ object Hashers {
351349
case RecordSelect(record, field) =>
352350
mixTag(TagRecordSelect)
353351
mixTree(record)
354-
mixFieldIdent(field)
352+
mixSimpleFieldIdent(field)
355353
mixType(tree.tpe)
356354

357355
case IsInstanceOf(expr, testType) =>
@@ -389,10 +387,9 @@ object Hashers {
389387
mixTree(ctor)
390388
mixTreeOrJSSpreads(args)
391389

392-
case JSPrivateSelect(qualifier, className, field) =>
390+
case JSPrivateSelect(qualifier, field) =>
393391
mixTag(TagJSPrivateSelect)
394392
mixTree(qualifier)
395-
mixName(className)
396393
mixFieldIdent(field)
397394

398395
case JSSelect(qualifier, item) =>
@@ -651,11 +648,18 @@ object Hashers {
651648
mixName(ident.name)
652649
}
653650

654-
def mixFieldIdent(ident: FieldIdent): Unit = {
651+
def mixSimpleFieldIdent(ident: SimpleFieldIdent): Unit = {
655652
mixPos(ident.pos)
656653
mixName(ident.name)
657654
}
658655

656+
def mixFieldIdent(ident: FieldIdent): Unit = {
657+
// For historical reasons, the className comes *before* the position
658+
mixName(ident.name.className)
659+
mixPos(ident.pos)
660+
mixName(ident.name.simpleName)
661+
}
662+
659663
def mixMethodIdent(ident: MethodIdent): Unit = {
660664
mixPos(ident.pos)
661665
mixMethodName(ident.name)

ir/shared/src/main/scala/org/scalajs/ir/Names.scala

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ object Names {
9999
def apply(name: String): LocalName =
100100
LocalName(UTF8String(name))
101101

102-
private[Names] def fromFieldName(name: FieldName): LocalName =
102+
private[Names] def fromSimpleFieldName(name: SimpleFieldName): LocalName =
103103
new LocalName(name.encoded)
104104
}
105105

@@ -137,38 +137,90 @@ object Names {
137137
LabelName(UTF8String(name))
138138
}
139139

140-
/** The name of a field.
140+
/** The simple name of a field (excluding its enclosing class).
141141
*
142142
* Field names must be non-empty, and can contain any Unicode code point
143143
* except `/ . ; [`.
144144
*/
145-
final class FieldName private (encoded: UTF8String)
146-
extends Name(encoded) with Comparable[FieldName] {
145+
final class SimpleFieldName private (encoded: UTF8String)
146+
extends Name(encoded) with Comparable[SimpleFieldName] {
147147

148-
type ThisName = FieldName
148+
type ThisName = SimpleFieldName
149149

150150
override def equals(that: Any): Boolean = {
151151
(this eq that.asInstanceOf[AnyRef]) || (that match {
152-
case that: FieldName => equalsName(that)
153-
case _ => false
152+
case that: SimpleFieldName => equalsName(that)
153+
case _ => false
154154
})
155155
}
156156

157-
protected def stringPrefix: String = "FieldName"
157+
protected def stringPrefix: String = "SimpleFieldName"
158158

159-
final def withSuffix(suffix: String): FieldName =
160-
FieldName(this.encoded ++ UTF8String(suffix))
159+
final def withSuffix(suffix: String): SimpleFieldName =
160+
SimpleFieldName(this.encoded ++ UTF8String(suffix))
161161

162162
final def toLocalName: LocalName =
163-
LocalName.fromFieldName(this)
163+
LocalName.fromSimpleFieldName(this)
164164
}
165165

166-
object FieldName {
167-
def apply(name: UTF8String): FieldName =
168-
new FieldName(validateSimpleEncodedName(name))
166+
object SimpleFieldName {
167+
def apply(name: UTF8String): SimpleFieldName =
168+
new SimpleFieldName(validateSimpleEncodedName(name))
169+
170+
def apply(name: String): SimpleFieldName =
171+
SimpleFieldName(UTF8String(name))
172+
}
169173

170-
def apply(name: String): FieldName =
171-
FieldName(UTF8String(name))
174+
/** The full name of a field, including its simple name and its enclosing
175+
* class name.
176+
*/
177+
final class FieldName private (
178+
val className: ClassName, val simpleName: SimpleFieldName)
179+
extends Comparable[FieldName] {
180+
181+
import FieldName._
182+
183+
private val _hashCode: Int = {
184+
import scala.util.hashing.MurmurHash3._
185+
var acc = -1025990011 // "FieldName".hashCode()
186+
acc = mix(acc, className.##)
187+
acc = mix(acc, simpleName.##)
188+
finalizeHash(acc, 2)
189+
}
190+
191+
override def equals(that: Any): Boolean = {
192+
(this eq that.asInstanceOf[AnyRef]) || (that match {
193+
case that: FieldName =>
194+
this._hashCode == that._hashCode && // fail fast on different hash codes
195+
this.className == that.className &&
196+
this.simpleName == that.simpleName
197+
case _ =>
198+
false
199+
})
200+
}
201+
202+
override def hashCode(): Int = _hashCode
203+
204+
def compareTo(that: FieldName): Int = {
205+
val classNameCmp = this.className.compareTo(that.className)
206+
if (classNameCmp != 0)
207+
classNameCmp
208+
else
209+
this.simpleName.compareTo(that.simpleName)
210+
}
211+
212+
protected def stringPrefix: String = "FieldName"
213+
214+
def nameString: String =
215+
className.nameString + "::" + simpleName.nameString
216+
217+
override def toString(): String =
218+
"FieldName<" + nameString + ">"
219+
}
220+
221+
object FieldName {
222+
def apply(className: ClassName, simpleName: SimpleFieldName): FieldName =
223+
new FieldName(className, simpleName)
172224
}
173225

174226
/** The simple name of a method (excluding its signature).

ir/shared/src/main/scala/org/scalajs/ir/OriginalName.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ final class OriginalName private (private val bytes: Array[Byte])
5353
if (isDefined) this
5454
else OriginalName(name)
5555

56+
// new in 1.16.0; added as last overload to preserve binary compatibility
57+
def orElse(name: FieldName): OriginalName =
58+
orElse(name.simpleName)
59+
5660
def getOrElse(name: Name): UTF8String =
5761
getOrElse(name.encoded)
5862

@@ -71,6 +75,10 @@ final class OriginalName private (private val bytes: Array[Byte])
7175
else UTF8String(name)
7276
}
7377

78+
// new in 1.16.0; added as last overload to preserve binary compatibility
79+
def getOrElse(name: FieldName): UTF8String =
80+
getOrElse(name.simpleName)
81+
7482
override def toString(): String =
7583
if (isDefined) s"OriginalName($unsafeGet)"
7684
else "NoOriginalName"

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

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ object Printers {
123123
node match {
124124
case node: LocalIdent => print(node)
125125
case node: LabelIdent => print(node)
126+
case node: SimpleFieldIdent => print(node)
126127
case node: FieldIdent => print(node)
127128
case node: MethodIdent => print(node)
128129
case node: ClassIdent => print(node)
@@ -299,16 +300,12 @@ object Printers {
299300
case StoreModule() =>
300301
print("<storeModule>")
301302

302-
case Select(qualifier, className, field) =>
303+
case Select(qualifier, field) =>
303304
print(qualifier)
304305
print('.')
305-
print(className)
306-
print("::")
307306
print(field)
308307

309-
case SelectStatic(className, field) =>
310-
print(className)
311-
print("::")
308+
case SelectStatic(field) =>
312309
print(field)
313310

314311
case SelectJSNativeMember(className, member) =>
@@ -572,11 +569,11 @@ object Printers {
572569

573570
case JSNew(ctor, args) =>
574571
def containsOnlySelectsFromAtom(tree: Tree): Boolean = tree match {
575-
case JSPrivateSelect(qual, _, _) => containsOnlySelectsFromAtom(qual)
576-
case JSSelect(qual, _) => containsOnlySelectsFromAtom(qual)
577-
case VarRef(_) => true
578-
case This() => true
579-
case _ => false // in particular, Apply
572+
case JSPrivateSelect(qual, _) => containsOnlySelectsFromAtom(qual)
573+
case JSSelect(qual, _) => containsOnlySelectsFromAtom(qual)
574+
case VarRef(_) => true
575+
case This() => true
576+
case _ => false // in particular, Apply
580577
}
581578
if (containsOnlySelectsFromAtom(ctor)) {
582579
print("new ")
@@ -588,11 +585,9 @@ object Printers {
588585
}
589586
printArgs(args)
590587

591-
case JSPrivateSelect(qualifier, className, field) =>
588+
case JSPrivateSelect(qualifier, field) =>
592589
print(qualifier)
593590
print('.')
594-
print(className)
595-
print("::")
596591
print(field)
597592

598593
case JSSelect(qualifier, item) =>
@@ -1113,6 +1108,9 @@ object Printers {
11131108
def print(ident: LabelIdent): Unit =
11141109
print(ident.name)
11151110

1111+
def print(ident: SimpleFieldIdent): Unit =
1112+
print(ident.name)
1113+
11161114
def print(ident: FieldIdent): Unit =
11171115
print(ident.name)
11181116

@@ -1125,6 +1123,9 @@ object Printers {
11251123
def print(name: Name): Unit =
11261124
printEscapeJS(name.nameString, out)
11271125

1126+
def print(name: FieldName): Unit =
1127+
printEscapeJS(name.nameString, out)
1128+
11281129
def print(name: MethodName): Unit =
11291130
printEscapeJS(name.nameString, out)
11301131

0 commit comments

Comments
 (0)