@@ -9,52 +9,84 @@ import dotty.tools.dotc.core.Symbols._
9
9
import dotty .tools .dotc .transform .MegaPhase .MiniPhase
10
10
11
11
/**
12
- * Created by wojtekswiderski on 2018-01-24.
12
+ * MiniPhase to transform s and raw string interpolators from using StringContext to string
13
+ * concatenation. Since string concatenation uses the Java String builder, we get a performance
14
+ * improvement in terms of these two interpolators.
15
+ *
16
+ * More info here:
17
+ * https://medium.com/@dkomanov/scala-string-interpolation-performance-21dc85e83afd
13
18
*/
14
19
class StringInterpolatorOpt extends MiniPhase {
15
20
import tpd ._
16
21
17
22
override def phaseName : String = " stringInterpolatorOpt"
18
23
24
+ /** Matches a list of constant literals */
25
+ private object Literals {
26
+ def unapply (tree : SeqLiteral )(implicit ctx : Context ): Option [List [Literal ]] = {
27
+ tree.elems match {
28
+ case literals if literals.forall(_.isInstanceOf [Literal ]) =>
29
+ Some (literals.map(_.asInstanceOf [Literal ]))
30
+ case _ => None
31
+ }
32
+ }
33
+ }
34
+
35
+ /** Matches an s or raw string interpolator */
36
+ private object SOrRawInterpolator {
37
+ def unapply (tree : Tree )(implicit ctx : Context ): Option [(List [Literal ], List [Tree ])] = {
38
+ if (tree.symbol.eq(defn.StringContextRaw ) || tree.symbol.eq(defn.StringContextS )) {
39
+ tree match {
40
+ case Apply (Select (Apply (strContextApply, List (Literals (strs))), _),
41
+ List (SeqLiteral (elems, _)))
42
+ if strContextApply.symbol.eq(defn.StringContextModule_apply ) &&
43
+ elems.length == strs.length - 1 =>
44
+ Some (strs, elems)
45
+ case _ => None
46
+ }
47
+ } else None
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Match trees that resemble s and raw string interpolations. In the case of the s
53
+ * interpolator, escapes the string constants. Exposes the string constants as well as
54
+ * the variable references.
55
+ */
19
56
private object StringContextIntrinsic {
20
- def unapply (tree : Apply )(implicit ctx : Context ): Option [(List [Tree ], List [Tree ])] = {
57
+ def unapply (tree : Apply )(implicit ctx : Context ): Option [(List [Literal ], List [Tree ])] = {
21
58
tree match {
22
- case Apply (Select (Apply (Select (ident, nme.apply), List (SeqLiteral (strs, _))), fn),
23
- List (SeqLiteral (elems, _))) =>
24
- if (ident.symbol.eq(defn.StringContextModule ) && strs.forall(_.isInstanceOf [Literal ])
25
- && elems.length == strs.length - 1 ) {
26
- if (fn == nme.raw_) Some (strs, elems)
27
- else if (fn == nme.s) {
28
- try {
29
- val escapedStrs = strs.mapConserve { str =>
30
- val strValue = str.asInstanceOf [Literal ].const.stringValue
31
- val escapedValue = StringContext .processEscapes(strValue)
32
- cpy.Literal (str)(Constant (escapedValue))
33
- }
34
- Some (escapedStrs, elems)
35
- } catch {
36
- case _ : StringContext .InvalidEscapeException => None
59
+ case SOrRawInterpolator (strs, elems) =>
60
+ if (tree.symbol == defn.StringContextRaw ) Some (strs, elems)
61
+ else { // tree.symbol == defn.StringContextS
62
+ try {
63
+ val escapedStrs = strs.map { str =>
64
+ val escapedValue = StringContext .processEscapes(str.const.stringValue)
65
+ cpy.Literal (str)(Constant (escapedValue))
37
66
}
38
- } else None
39
- } else None
67
+ Some (escapedStrs, elems)
68
+ } catch {
69
+ case _ : StringContext .InvalidEscapeException => None
70
+ }
71
+ }
40
72
case _ => None
41
73
}
42
74
}
43
75
}
44
76
45
77
override def transformApply (tree : Apply )(implicit ctx : Context ): Tree = {
46
78
tree match {
47
- case StringContextIntrinsic (strs : List [Tree ], elems : List [Tree ]) =>
79
+ case StringContextIntrinsic (strs : List [Literal ], elems : List [Tree ]) =>
48
80
val stri = strs.iterator
49
81
val elemi = elems.iterator
50
- var result = stri.next
82
+ var result : Tree = stri.next
51
83
def concat (tree : Tree ): Unit = {
52
84
result = result.select(defn.String_+ ).appliedTo(tree)
53
85
}
54
86
while (elemi.hasNext) {
55
87
concat(elemi.next)
56
88
val str = stri.next
57
- if (! str.asInstanceOf [ Literal ]. const.stringValue.isEmpty) concat(str)
89
+ if (! str.const.stringValue.isEmpty) concat(str)
58
90
}
59
91
result
60
92
case _ => tree
0 commit comments