Skip to content

Commit 9790f98

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

File tree

2 files changed

+52
-32
lines changed

2 files changed

+52
-32
lines changed

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

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -95,58 +95,79 @@ 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 nameHash = if $name == null then 0 else $name.hashCode
101+
* var ordinal = -1
102+
* nameHash match {
103+
* case 100054 =>
104+
* if "e_0" equals $name then
105+
* ordinal = 0
106+
* ...
107+
* case 100116 =>
108+
* if "e_n" equals $name then
109+
* ordinal = n
110+
* case _ =>
111+
* }
112+
* ordinal match {
113+
* case 0 => e_0
114+
* ...
115+
* case n => e_n
116+
* case _ => throw new IllegalArgumentException("case not found: " + $name)
117+
* }
107118
*/
108119
private def enumScaffolding(enumCases: List[(Int, TermName)])(using Context): List[Tree] = {
109120
import dotty.tools.dotc.transform.SymUtils.rawTypeRef
121+
122+
def const(arg: String | Int | Null | Unit) = Literal(Constant(arg))
123+
110124
val rawEnumClassRef = rawRef(enumClass.typeRef)
111125
extension (tpe: NamedType) def ofRawEnum = AppliedTypeTree(ref(tpe), rawEnumClassRef)
112126

113127
val privateValuesDef =
114128
ValDef(nme.DOLLAR_VALUES, TypeTree(), JavaSeqLiteral(enumCases.map((_, name) => Ident(name)), rawEnumClassRef))
115129
.withFlags(Private | Synthetic)
116130

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-
128131
val valuesDef =
129132
DefDef(nme.values, Nil, Nil, defn.ArrayType.ofRawEnum, valuesDot(nme.clone_))
130133
.withFlags(Synthetic)
131134

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-
)
135+
val valuesOfBody: Tree =
136+
val nameHash = nme.syntheticParamName(0)
137+
val ordinal = nme.syntheticParamName(1)
138+
val nameHashDef =
139+
ValDef(nameHash, TypeTree(defn.IntType), If(
140+
cond = Apply(Select(Ident(nme.nameDollar), nme.eq), const(null)),
141+
thenp = const(0),
142+
elsep = Select(Ident(nme.nameDollar), nme.hashCode_))).withFlags(Local)
143+
val ordinalDef = ValDef(ordinal, TypeTree(defn.IntType), const(-1)).withFlags(Local | Mutable)
144+
val hashCases = enumCases.map { (ord, name) =>
145+
val condAssign = If(
146+
cond = Apply(Select(const(name.toString), nme.equals_), Ident(nme.nameDollar)),
147+
thenp = Assign(Ident(ordinal), const(ord)),
148+
elsep = const(()))
149+
CaseDef(const(name.toString.hashCode), EmptyTree, condAssign)
150+
} ::: CaseDef(Ident(nme.WILDCARD), EmptyTree, const(())) :: Nil
151+
val finalDefault =
152+
val msg = Apply(Select(const("enum case not found: "), nme.PLUS), Ident(nme.nameDollar))
153+
CaseDef(
154+
pat = Ident(nme.WILDCARD),
155+
guard = EmptyTree,
156+
body = Throw(New(TypeTree(defn.IllegalArgumentExceptionType), List(msg :: Nil))))
157+
val intermediateCases = enumCases.map((ordinal, name) =>
158+
CaseDef(const(ordinal), EmptyTree, Ident(name))
159+
) ::: finalDefault :: Nil
160+
Block(
161+
nameHashDef ::
162+
ordinalDef ::
163+
Match(Ident(nameHash), hashCases) :: Nil,
164+
Match(Ident(ordinal), intermediateCases)
165+
)
144166
val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.nameDollar, defn.StringType) :: Nil),
145167
TypeTree(), valuesOfBody)
146168
.withFlags(Synthetic)
147169

148170
privateValuesDef ::
149-
privateReverseValuesDef ::
150171
valuesDef ::
151172
valueOfDef :: Nil
152173
}

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)