Skip to content

Commit 09d0d90

Browse files
authored
Merge pull request scala-js#3804 from sjrd/dedicated-types-for-names
Introduce dedicated types for the kinds of `Ident`s and their `Name`s.
2 parents 1531a86 + a4a0f3a commit 09d0d90

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1522
-1106
lines changed

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

Lines changed: 57 additions & 49 deletions
Large diffs are not rendered by default.

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import scala.reflect.{ClassTag, classTag}
2020
import scala.reflect.internal.Flags
2121

2222
import org.scalajs.ir
23-
import ir.{Trees => js, Types => jstpe}
24-
import ir.Trees.OptimizerHints
23+
import org.scalajs.ir.{Definitions => defs, Trees => js, Types => jstpe}
24+
import org.scalajs.ir.Trees.OptimizerHints
2525

2626
import org.scalajs.nscplugin.util.ScopedVar
2727
import ScopedVar.withScopedVars
@@ -667,7 +667,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent {
667667
val superClass = {
668668
val superClassSym = currentClassSym.superClass
669669
if (isNestedJSClass(superClassSym)) {
670-
js.VarRef(js.Ident(JSSuperClassParamName))(jstpe.AnyType)
670+
js.VarRef(js.LocalIdent(JSSuperClassParamName))(jstpe.AnyType)
671671
} else {
672672
js.LoadJSConstructor(encodeClassRef(superClassSym))
673673
}
@@ -1105,19 +1105,19 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent {
11051105
}
11061106

11071107
private def genFormalArg(index: Int)(implicit pos: Position): js.ParamDef = {
1108-
js.ParamDef(js.Ident("arg$" + index), jstpe.AnyType,
1108+
js.ParamDef(js.LocalIdent(defs.LocalName("arg$" + index)), jstpe.AnyType,
11091109
mutable = false, rest = false)
11101110
}
11111111

11121112
private def genRestFormalArg()(implicit pos: Position): js.ParamDef = {
1113-
js.ParamDef(js.Ident("arg$rest"), jstpe.AnyType,
1113+
js.ParamDef(js.LocalIdent(defs.LocalName("arg$rest")), jstpe.AnyType,
11141114
mutable = false, rest = true)
11151115
}
11161116

11171117
private def genFormalArgRef(index: Int, minArgc: Int)(
11181118
implicit pos: Position): js.Tree = {
11191119
if (index <= minArgc)
1120-
js.VarRef(js.Ident("arg$" + index))(jstpe.AnyType)
1120+
js.VarRef(js.LocalIdent(defs.LocalName("arg$" + index)))(jstpe.AnyType)
11211121
else
11221122
js.JSSelect(genRestArgRef(), js.IntLiteral(index - 1 - minArgc))
11231123
}
@@ -1135,7 +1135,7 @@ trait GenJSExports[G <: Global with Singleton] extends SubComponent {
11351135
}
11361136

11371137
private def genRestArgRef()(implicit pos: Position): js.Tree =
1138-
js.VarRef(js.Ident("arg$rest"))(jstpe.AnyType)
1138+
js.VarRef(js.LocalIdent(defs.LocalName("arg$rest")))(jstpe.AnyType)
11391139

11401140
private def hasRepeatedParam(sym: Symbol) = {
11411141
enteringPhase(currentRun.uncurryPhase) {

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

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,12 @@ import scala.collection.mutable
1717
import scala.tools.nsc._
1818

1919
import org.scalajs.ir
20-
import ir.{Trees => js, Types => jstpe}
20+
import org.scalajs.ir.{Definitions => defs, Trees => js, Types => jstpe}
2121

2222
import org.scalajs.nscplugin.util.{ScopedVar, VarBox}
2323
import ScopedVar.withScopedVars
2424

25-
/** Encoding of symbol names for JavaScript
26-
*
27-
* Some issues that this encoding solves:
28-
* * Overloading: encode the full signature in the JS name
29-
* * Same scope for fields and methods of a class
30-
* * Global access to classes and modules (by their full name)
31-
*
32-
* @author Sébastien Doeraene
33-
*/
25+
/** Encoding of symbol names for the IR. */
3426
trait JSEncoding[G <: Global with Singleton] extends SubComponent {
3527
self: GenJSCode[G] =>
3628

@@ -51,24 +43,28 @@ trait JSEncoding[G <: Global with Singleton] extends SubComponent {
5143
* local name scope using [[reserveLocalName]]. Otherwise, this name can
5244
* clash with another local identifier.
5345
*/
54-
final val JSSuperClassParamName = "$superClass"
46+
final val JSSuperClassParamName = defs.LocalName("$superClass")
5547

5648
// Fresh local name generator ----------------------------------------------
5749

5850
private val usedLocalNames = new ScopedVar[mutable.Set[String]]
59-
private val returnLabelName = new ScopedVar[VarBox[Option[String]]]
60-
private val localSymbolNames = new ScopedVar[mutable.Map[Symbol, String]]
51+
private val localSymbolNames = new ScopedVar[mutable.Map[Symbol, defs.LocalName]]
52+
private val usedLabelNames = new ScopedVar[mutable.Set[String]]
53+
private val labelSymbolNames = new ScopedVar[mutable.Map[Symbol, defs.LabelName]]
54+
private val returnLabelName = new ScopedVar[VarBox[Option[defs.LabelName]]]
6155
private val isReserved = Set("arguments", "eval")
6256

6357
def withNewLocalNameScope[A](body: => A): A = {
6458
withScopedVars(
6559
usedLocalNames := mutable.Set.empty,
66-
returnLabelName := null,
67-
localSymbolNames := mutable.Map.empty
60+
localSymbolNames := mutable.Map.empty,
61+
usedLabelNames := mutable.Set.empty,
62+
labelSymbolNames := mutable.Map.empty,
63+
returnLabelName := null
6864
)(body)
6965
}
7066

71-
def reserveLocalName(name: String): Unit = {
67+
def reserveLocalName(name: defs.LocalName): Unit = {
7268
require(usedLocalNames.isEmpty,
7369
s"Trying to reserve the name '$name' but names have already been " +
7470
"allocated")
@@ -85,48 +81,61 @@ trait JSEncoding[G <: Global with Singleton] extends SubComponent {
8581
case None =>
8682
inner
8783
case Some(labelName) =>
88-
js.Labeled(js.Ident(labelName), tpe, inner)
84+
js.Labeled(js.LabelIdent(labelName), tpe, inner)
8985
}
9086
}
9187
}
9288

93-
private def freshName(base: String = "x"): String = {
89+
private def freshNameGeneric[N <: String](base: String,
90+
usedNamesSet: mutable.Set[String], createNameFun: String => N): N = {
9491
var suffix = 1
9592
var longName = base
96-
while (usedLocalNames(longName) || isReserved(longName)) {
93+
while (usedNamesSet(longName) || isReserved(longName)) {
9794
suffix += 1
98-
longName = base+"$"+suffix
95+
longName = base + "$" + suffix
9996
}
100-
usedLocalNames += longName
101-
mangleJSName(longName)
97+
usedNamesSet += longName
98+
createNameFun(mangleJSName(longName))
10299
}
103100

104-
def freshLocalIdent()(implicit pos: ir.Position): js.Ident =
105-
js.Ident(freshName(), None)
101+
private def freshName(base: String): defs.LocalName =
102+
freshNameGeneric(base, usedLocalNames, defs.LocalName(_))
106103

107-
def freshLocalIdent(base: String)(implicit pos: ir.Position): js.Ident =
108-
js.Ident(freshName(base), Some(base))
104+
def freshLocalIdent()(implicit pos: ir.Position): js.LocalIdent =
105+
js.LocalIdent(freshName("x"), None)
109106

110-
private def localSymbolName(sym: Symbol): String =
107+
def freshLocalIdent(base: String)(implicit pos: ir.Position): js.LocalIdent =
108+
js.LocalIdent(freshName(base), Some(base))
109+
110+
private def localSymbolName(sym: Symbol): defs.LocalName =
111111
localSymbolNames.getOrElseUpdate(sym, freshName(sym.name.toString))
112112

113-
def getEnclosingReturnLabel()(implicit pos: ir.Position): js.Ident = {
113+
private def freshLabelName(base: String): defs.LabelName =
114+
freshNameGeneric(base, usedLabelNames, defs.LabelName(_))
115+
116+
def freshLabelIdent(base: String)(implicit pos: ir.Position): js.LabelIdent =
117+
js.LabelIdent(freshLabelName(base))
118+
119+
private def labelSymbolName(sym: Symbol): defs.LabelName =
120+
labelSymbolNames.getOrElseUpdate(sym, freshLabelName(sym.name.toString))
121+
122+
def getEnclosingReturnLabel()(implicit pos: ir.Position): js.LabelIdent = {
114123
val box = returnLabelName.get
115124
if (box == null)
116125
throw new IllegalStateException(s"No enclosing returnable scope at $pos")
117126
if (box.value.isEmpty)
118-
box.value = Some(freshName("_return"))
119-
js.Ident(box.value.get)
127+
box.value = Some(freshLabelName("_return"))
128+
js.LabelIdent(box.value.get)
120129
}
121130

122131
// Encoding methods ----------------------------------------------------------
123132

124-
def encodeLabelSym(sym: Symbol)(implicit pos: Position): js.Ident = {
133+
def encodeLabelSym(sym: Symbol)(implicit pos: Position): js.LabelIdent = {
125134
require(sym.isLabel, "encodeLabelSym called with non-label symbol: " + sym)
126-
js.Ident(localSymbolName(sym), Some(sym.unexpandedName.decoded))
135+
js.LabelIdent(labelSymbolName(sym))
127136
}
128137

129-
def encodeFieldSym(sym: Symbol)(implicit pos: Position): js.Ident = {
138+
def encodeFieldSym(sym: Symbol)(implicit pos: Position): js.FieldIdent = {
130139
require(sym.owner.isClass && sym.isTerm && !sym.isMethod && !sym.isModule,
131140
"encodeFieldSym called with non-field symbol: " + sym)
132141

@@ -135,11 +144,12 @@ trait JSEncoding[G <: Global with Singleton] extends SubComponent {
135144
if (name0.charAt(name0.length()-1) != ' ') name0
136145
else name0.substring(0, name0.length()-1)
137146

138-
js.Ident(mangleJSName(name), Some(sym.unexpandedName.decoded))
147+
js.FieldIdent(defs.FieldName(mangleJSName(name)),
148+
Some(sym.unexpandedName.decoded))
139149
}
140150

141151
def encodeMethodSym(sym: Symbol, reflProxy: Boolean = false)(
142-
implicit pos: Position): js.Ident = {
152+
implicit pos: Position): js.MethodIdent = {
143153

144154
require(sym.isMethod,
145155
"encodeMethodSym called with non-method symbol: " + sym)
@@ -150,19 +160,20 @@ trait JSEncoding[G <: Global with Singleton] extends SubComponent {
150160

151161
val paramsString = makeParamsString(sym, reflProxy)
152162

153-
js.Ident(encodedName + paramsString,
163+
js.MethodIdent(defs.MethodName(encodedName + paramsString),
154164
Some(sym.unexpandedName.decoded + paramsString))
155165
}
156166

157-
def encodeStaticMemberSym(sym: Symbol)(implicit pos: Position): js.Ident = {
167+
def encodeStaticMemberSym(sym: Symbol)(implicit pos: Position): js.MethodIdent = {
158168
require(sym.isStaticMember,
159169
"encodeStaticMemberSym called with non-static symbol: " + sym)
160-
js.Ident(
161-
mangleJSName(encodeMemberNameInternal(sym)) + "__" + internalName(sym.tpe),
170+
js.MethodIdent(
171+
defs.MethodName(
172+
mangleJSName(encodeMemberNameInternal(sym)) + "__" + internalName(sym.tpe)),
162173
Some(sym.unexpandedName.decoded))
163174
}
164175

165-
def encodeLocalSym(sym: Symbol)(implicit pos: Position): js.Ident = {
176+
def encodeLocalSym(sym: Symbol)(implicit pos: Position): js.LocalIdent = {
166177
/* The isValueParameter case is necessary to work around an internal bug
167178
* of scalac: for some @varargs methods, the owner of some parameters is
168179
* the enclosing class rather the method, so !sym.owner.isClass fails.
@@ -172,7 +183,7 @@ trait JSEncoding[G <: Global with Singleton] extends SubComponent {
172183
require(sym.isValueParameter ||
173184
(!sym.owner.isClass && sym.isTerm && !sym.isMethod && !sym.isModule),
174185
"encodeLocalSym called with non-local symbol: " + sym)
175-
js.Ident(localSymbolName(sym), Some(sym.unexpandedName.decoded))
186+
js.LocalIdent(localSymbolName(sym), Some(sym.unexpandedName.decoded))
176187
}
177188

178189
def foreignIsImplClass(sym: Symbol): Boolean =
@@ -191,24 +202,26 @@ trait JSEncoding[G <: Global with Singleton] extends SubComponent {
191202
def encodeClassRef(sym: Symbol): jstpe.ClassRef =
192203
jstpe.ClassRef(encodeClassFullName(sym))
193204

194-
def encodeClassFullNameIdent(sym: Symbol)(implicit pos: Position): js.Ident = {
195-
js.Ident(encodeClassFullName(sym), Some(sym.fullName))
196-
}
205+
def encodeClassFullNameIdent(sym: Symbol)(implicit pos: Position): js.ClassIdent =
206+
js.ClassIdent(encodeClassFullName(sym), Some(sym.fullName))
207+
208+
private val BoxedStringModuleClassName = defs.ClassName("jl_String$")
209+
private val BoxedVoidModuleClassName = defs.ClassName("jl_Void$")
197210

198-
def encodeClassFullName(sym: Symbol): String = {
211+
def encodeClassFullName(sym: Symbol): defs.ClassName = {
199212
assert(!sym.isPrimitiveValueClass,
200213
s"Illegal encodeClassFullName(${sym.fullName}")
201214
if (sym == jsDefinitions.HackedStringClass) {
202215
ir.Definitions.BoxedStringClass
203216
} else if (sym == jsDefinitions.HackedStringModClass) {
204-
"jl_String$"
217+
BoxedStringModuleClassName
205218
} else if (sym == definitions.BoxedUnitClass) {
206219
// Rewire scala.runtime.BoxedUnit to java.lang.Void, as the IR expects
207220
// BoxedUnit$ is a JVM artifact
208221
ir.Definitions.BoxedUnitClass
209222
} else if (sym == jsDefinitions.BoxedUnitModClass) {
210223
// Same for its module class
211-
"jl_Void$"
224+
BoxedVoidModuleClassName
212225
} else {
213226
ir.Definitions.encodeClassName(
214227
sym.fullName + (if (needsModuleClassSuffix(sym)) "$" else ""))

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import scala.collection.mutable
1616

1717
import scala.tools.nsc.Global
1818

19-
import org.scalajs.ir.Trees.isValidIdentifier
19+
import org.scalajs.ir.Trees.isValidJSIdentifier
2020

2121
/**
2222
* Prepare export generation
@@ -324,7 +324,7 @@ trait PrepJSExports[G <: Global with Singleton] { this: PrepJSInterop[G] =>
324324
}
325325

326326
// The top-level name must be a valid JS identifier
327-
if (!isValidIdentifier(name)) {
327+
if (!isValidJSIdentifier(name)) {
328328
reporter.error(annot.pos,
329329
"The top-level export name must be a valid JavaScript " +
330330
"identifier")

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import nsc._
1818
import scala.collection.immutable.ListMap
1919
import scala.collection.mutable
2020

21-
import org.scalajs.ir.Trees.{isValidIdentifier, JSNativeLoadSpec}
21+
import org.scalajs.ir.Trees.{isValidJSIdentifier, JSNativeLoadSpec}
2222

2323
/** Prepares classes extending js.Any for JavaScript interop
2424
*
@@ -725,7 +725,7 @@ abstract class PrepJSInterop[G <: Global with Singleton](val global: G)
725725

726726
def parseGlobalPath(pathName: String): Global = {
727727
val globalRef :: path = parsePath(pathName)
728-
if (!isValidIdentifier(globalRef)) {
728+
if (!isValidJSIdentifier(globalRef)) {
729729
reporter.error(pos,
730730
"The name of a JS global variable must be a valid JS " +
731731
s"identifier (got '$globalRef')")

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ package org.scalajs.nscplugin
1414

1515
import scala.tools.nsc._
1616

17-
import org.scalajs.ir
18-
import ir.{Definitions, Types}
17+
import org.scalajs.ir.Types
1918

2019
/** Conversions from scalac `Type`s to the IR `Type`s and `TypeRef`s. */
2120
trait TypeConversions[G <: Global with Singleton] extends SubComponent {

compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class OptimizationTest extends JSASTTest {
5555
}
5656
""".
5757
hasExactly(2, "calls to Array.apply methods") {
58-
case js.Apply(_, js.LoadModule(jstpe.ClassRef("s_Array$")), js.Ident(methodName, _), _)
58+
case js.Apply(_, js.LoadModule(jstpe.ClassRef("s_Array$")), js.MethodIdent(methodName, _), _)
5959
if methodName.startsWith("apply__") =>
6060
}
6161
}

0 commit comments

Comments
 (0)