Skip to content

Commit 6189ddc

Browse files
committed
Refactor checks for illegal characters in JVM names
The only check we actually need is whether an encoded name is valid or not, so get rid of the unusued isValidJVMChar and replace isValidJVMMethodChar by isValidInEncodedName, the only difference in the latter is that ':' is also prohibited which doesn't change anything for Scala since we encode ':' to "$colon" anyway.
1 parent e0aac8f commit 6189ddc

File tree

4 files changed

+53
-16
lines changed

4 files changed

+53
-16
lines changed

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import typer.ErrorReporting._
1818
import reporting.ThrowingReporter
1919
import ast.Trees._
2020
import ast.{tpd, untpd}
21-
import scala.tasty.util.Chars._
21+
import scala.tasty.util.Chars
2222
import collection.mutable
2323
import ProtoTypes._
2424

@@ -42,9 +42,8 @@ class TreeChecker extends Phase with SymTransformer {
4242
private val seenClasses = collection.mutable.HashMap[String, Symbol]()
4343
private val seenModuleVals = collection.mutable.HashMap[String, Symbol]()
4444

45-
def isValidJVMName(name: Name): Boolean = name.toString.forall(isValidJVMChar)
46-
47-
def isValidJVMMethodName(name: Name): Boolean = name.toString.forall(isValidJVMMethodChar)
45+
def isValidEncodedName(name: Name): Boolean =
46+
name.toString.forall(Chars.isValidInEncodedName)
4847

4948
val NoSuperClass: FlagSet = Trait | Package
5049

@@ -142,7 +141,8 @@ class TreeChecker extends Phase with SymTransformer {
142141
val sym = tree.symbol
143142
tree match {
144143
case tree: untpd.DefTree =>
145-
assert(isValidJVMName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm")
144+
assert(isValidEncodedName(sym.name.encode),
145+
s"${sym.name.debugString} encoded name contains invalid character(s)")
146146
everDefinedSyms.get(sym) match {
147147
case Some(t) =>
148148
if (t ne tree)
@@ -387,8 +387,9 @@ class TreeChecker extends Phase with SymTransformer {
387387
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context): Tree =
388388
withDefinedSyms(ddef.tparams) {
389389
withDefinedSyms(ddef.vparamss.flatten) {
390-
if (!sym.isClassConstructor && !(sym.name eq nme.STATIC_CONSTRUCTOR))
391-
assert(isValidJVMMethodName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm")
390+
assert((sym.name eq nme.CONSTRUCTOR) || (sym.name eq nme.STATIC_CONSTRUCTOR) ||
391+
isValidEncodedName(sym.name.encode),
392+
s"${sym.name.debugString} encoded name contains invalid character(s)")
392393

393394
ddef.vparamss.foreach(_.foreach { vparam =>
394395
assert(vparam.symbol.is(Param),

compiler/src/dotty/tools/dotc/util/NameTransformer.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,22 @@ object NameTransformer {
4242
enterOp('?', "$qmark")
4343
enterOp('@', "$at")
4444

45-
/** Expand characters that are illegal as JVM method names by `$u`, followed
46-
* by the character's unicode expansion.
45+
private def isOp(c: Char) = c < op2code.length && op2code(c) != null
46+
47+
/** Expand illegal characters by `$u` followed by the character's unicode expansion.
48+
* Illegal characters are those which are not valid in encoded name and won't
49+
* be replaced by `encode`.
4750
*/
4851
def avoidIllegalChars(name: SimpleName): SimpleName = {
52+
def isLegal(c: Char): Boolean = Chars.isValidInEncodedName(c) || isOp(c)
53+
4954
var i = name.length - 1
50-
while (i >= 0 && Chars.isValidJVMMethodChar(name(i))) i -= 1
55+
while (i >= 0 && isLegal(name(i))) i -= 1
56+
5157
if (i >= 0)
5258
termName(
5359
name.toString.flatMap(ch =>
54-
if (Chars.isValidJVMMethodChar(ch)) ch.toString else "$u%04X".format(ch.toInt)))
60+
if (isLegal(ch)) ch.toString else "$u%04X".format(ch.toInt)))
5561
else name
5662
}
5763

library/src/scala/tasty/util/Chars.scala

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,14 @@ object Chars {
7272
chtp == JCharacter.MATH_SYMBOL.toInt || chtp == JCharacter.OTHER_SYMBOL.toInt
7373
}
7474

75-
def isValidJVMChar(c: Char): Boolean =
76-
!(c == '.' || c == ';' || c =='[' || c == '/')
77-
78-
def isValidJVMMethodChar(c: Char): Boolean =
79-
!(c == '.' || c == ';' || c =='[' || c == '/' || c == '<' || c == '>')
75+
/** Can `c` appear in an encoded name ?
76+
* This is true for all characters, except those prohibited in JVM signatures
77+
* (https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1).
78+
*/
79+
def isValidInEncodedName(c: Char): Boolean = (c: @switch) match {
80+
case '.' | ';' | '[' | '/' | '<' | '>' | ':' => false
81+
case _ => true
82+
}
8083

8184
private final val otherLetters = Set[Char]('\u0024', '\u005F') // '$' and '_'
8285
private final val letterGroups = {

tests/run/special-names.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
class `.`
2+
class `;`
3+
class `[`
4+
class `/`
5+
class `<`
6+
class `>`
7+
class `:`
8+
9+
object Test {
10+
def `.`(x: `.`) = x
11+
def `;`(x: `;`) = x
12+
def `[`(x: `[`) = x
13+
def `/`(x: `/`) = x
14+
def `<`(x: `<`) = x
15+
def `>`(x: `>`) = x
16+
def `:`(x: `:`) = x
17+
18+
def main(args: Array[String]): Unit = {
19+
`.`(new `.`)
20+
`;`(new `;`)
21+
`[`(new `[`)
22+
`/`(new `/`)
23+
`<`(new `<`)
24+
`>`(new `>`)
25+
`:`(new `:`)
26+
}
27+
}

0 commit comments

Comments
 (0)