Skip to content

Commit b201183

Browse files
committed
fast switch on string for valueOf
1 parent e3a6237 commit b201183

File tree

2 files changed

+51
-32
lines changed

2 files changed

+51
-32
lines changed

compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -95,58 +95,78 @@ object DesugarEnums {
9595
/** The following lists of definitions for an enum type E and known value cases e_0, ..., e_n:
9696
*
9797
* private val $values = [e_0, ..., e_n : E]
98-
* @annotation.threadUnsafe private lazy val $valuesReverse =
99-
* scala.runtime.ScalaRuntime.wrapRefArray($values).map((x_0: E) => (x_0.enumLabel, x_0)).toMap
10098
* def values = $values.clone
10199
* def valueOf($name: String) =
102-
* try $valuesReverse($name) catch
103-
* {
104-
* case ex$: NoSuchElementException =>
105-
* throw new IllegalArgumentException("enum case not found: " + $name)
106-
* }
100+
* val h = if $name == null then 0 else $name.hashCode
101+
* var intermediate = -1
102+
* h match {
103+
* case 326467 =>
104+
* if "e_0" equals $name then
105+
* intermediate = 0
106+
* ...
107+
* case 426978 =>
108+
* if "e_n" equals $name then
109+
* intermediate = n
110+
* }
111+
* intermediate match {
112+
* case 0 => e_0
113+
* ...
114+
* case n => e_n
115+
* case _ => throw new IllegalArgumentException("case not found: " + $name)
116+
* }
107117
*/
108118
private def enumScaffolding(enumCases: List[(Int, TermName)])(using Context): List[Tree] = {
109119
import dotty.tools.dotc.transform.SymUtils.rawTypeRef
120+
121+
def const(arg: String | Int | Null | Unit) = Literal(Constant(arg))
122+
110123
val rawEnumClassRef = rawRef(enumClass.typeRef)
111124
extension (tpe: NamedType) def ofRawEnum = AppliedTypeTree(ref(tpe), rawEnumClassRef)
112125

113126
val privateValuesDef =
114127
ValDef(nme.DOLLAR_VALUES, TypeTree(), JavaSeqLiteral(enumCases.map((_, name) => Ident(name)), rawEnumClassRef))
115128
.withFlags(Private | Synthetic)
116129

117-
val privateReverseValuesDef =
118-
val wrapped = Apply(Select(ref(defn.ScalaRuntimeModule.termRef), nme.wrapRefArray), Ident(nme.DOLLAR_VALUES))
119-
val mapper =
120-
val paramName = nme.syntheticParamName(0)
121-
val paramDef = param(paramName, rawEnumClassRef)
122-
Function(paramDef :: Nil, Tuple(Select(Ident(paramName), nme.enumLabel) :: Ident(paramName) :: Nil))
123-
val mapBody = Select(Apply(Select(wrapped, nme.map), mapper), nme.toMap)
124-
val annot = New(ref(defn.ThreadUnsafeAnnot.typeRef), Nil).withSpan(ctx.tree.span)
125-
ValDef(nme.DOLLAR_VALUES_REVERSE, TypeTree(), mapBody)
126-
.withFlags(Private | Synthetic | Lazy).withAnnotations(annot :: Nil)
127-
128130
val valuesDef =
129131
DefDef(nme.values, Nil, Nil, defn.ArrayType.ofRawEnum, valuesDot(nme.clone_))
130132
.withFlags(Synthetic)
131133

132-
val valuesOfExnMessage = Apply(
133-
Select(Literal(Constant("enum case not found: ")), nme.PLUS), Ident(nme.nameDollar))
134-
135-
val valuesOfBody = Try(
136-
expr = Apply(Ident(nme.DOLLAR_VALUES_REVERSE), Ident(nme.nameDollar) :: Nil),
137-
cases = CaseDef(
138-
pat = Typed(Ident(nme.DEFAULT_EXCEPTION_NAME), TypeTree(defn.NoSuchElementExceptionType)),
139-
guard = EmptyTree,
140-
body = Throw(New(TypeTree(defn.IllegalArgumentExceptionType), List(valuesOfExnMessage :: Nil)))
141-
) :: Nil,
142-
finalizer = EmptyTree
143-
)
134+
val valuesOfBody: Tree =
135+
val nameHash = nme.syntheticParamName(0)
136+
val ordinal = nme.syntheticParamName(1)
137+
val nameHashDef =
138+
ValDef(nameHash, TypeTree(defn.IntType), If(
139+
cond = Apply(Select(Ident(nme.nameDollar), nme.eq), const(null)),
140+
thenp = const(0),
141+
elsep = Select(Ident(nme.nameDollar), nme.hashCode_))).withFlags(Local)
142+
val ordinalDef = ValDef(ordinal, TypeTree(defn.IntType), const(-1)).withFlags(Local | Mutable)
143+
val hashCases = enumCases.map { (ord, name) =>
144+
val condAssign = If(
145+
cond = Apply(Select(const(name.toString), nme.equals_), Ident(nme.nameDollar)),
146+
thenp = Assign(Ident(ordinal), const(ord)),
147+
elsep = const(()))
148+
CaseDef(const(name.toString.hashCode), EmptyTree, condAssign)
149+
} ::: CaseDef(Ident(nme.WILDCARD), EmptyTree, const(())) :: Nil
150+
val finalDefault =
151+
val msg = Apply(Select(const("enum case not found: "), nme.PLUS), Ident(nme.nameDollar))
152+
CaseDef(
153+
pat = Ident(nme.WILDCARD),
154+
guard = EmptyTree,
155+
body = Throw(New(TypeTree(defn.IllegalArgumentExceptionType), List(msg :: Nil))))
156+
val intermediateCases = enumCases.map((ordinal, name) =>
157+
CaseDef(const(ordinal), EmptyTree, Ident(name))
158+
) ::: finalDefault :: Nil
159+
Block(
160+
nameHashDef ::
161+
ordinalDef ::
162+
Match(Ident(nameHash), hashCases) :: Nil,
163+
Match(Ident(ordinal), intermediateCases)
164+
)
144165
val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.nameDollar, defn.StringType) :: Nil),
145166
TypeTree(), valuesOfBody)
146167
.withFlags(Synthetic)
147168

148169
privateValuesDef ::
149-
privateReverseValuesDef ::
150170
valuesDef ::
151171
valueOfDef :: Nil
152172
}

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ object StdNames {
123123
val DEFAULT_GETTER_INIT: N = "$lessinit$greater"
124124
val DO_WHILE_PREFIX: N = "doWhile$"
125125
val DOLLAR_VALUES: N = "$values"
126-
val DOLLAR_VALUES_REVERSE: N = "$valuesReverse"
127126
val DOLLAR_NEW: N = "$new"
128127
val EMPTY: N = ""
129128
val EMPTY_PACKAGE: N = "<empty>"

0 commit comments

Comments
 (0)