All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dotty.tools.dotc.transform.localopt.StringInterpolatorOpt.scala Maven / Gradle / Ivy

The newest version!
package dotty.tools.dotc.transform.localopt

import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Constants.Constant
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.transform.MegaPhase.MiniPhase

/**
  * MiniPhase to transform s and raw string interpolators from using StringContext to string
  * concatenation. Since string concatenation uses the Java String builder, we get a performance
  * improvement in terms of these two interpolators.
  *
  * More info here:
  * https://medium.com/@dkomanov/scala-string-interpolation-performance-21dc85e83afd
  */
class StringInterpolatorOpt extends MiniPhase {
  import tpd._

  override def phaseName: String = "stringInterpolatorOpt"

  /** Matches a list of constant literals */
  private object Literals {
    def unapply(tree: SeqLiteral)(implicit ctx: Context): Option[List[Literal]] = {
      tree.elems match {
        case literals if literals.forall(_.isInstanceOf[Literal]) =>
          Some(literals.map(_.asInstanceOf[Literal]))
        case _ => None
      }
    }
  }

  private object StringContextApply {
    def unapply(tree: Select)(implicit ctx: Context): Boolean = {
      tree.symbol.eq(defn.StringContextModule_apply) &&
      tree.qualifier.symbol.eq(defn.StringContextModule)
    }
  }

  /** Matches an s or raw string interpolator */
  private object SOrRawInterpolator {
    def unapply(tree: Tree)(implicit ctx: Context): Option[(List[Literal], List[Tree])] = {
      tree match {
        case Apply(Select(Apply(StringContextApply(), List(Literals(strs))), _),
        List(SeqLiteral(elems, _))) if elems.length == strs.length - 1 =>
          Some(strs, elems)
        case _ => None
      }
    }
  }

  /**
    * Match trees that resemble s and raw string interpolations. In the case of the s
    * interpolator, escapes the string constants. Exposes the string constants as well as
    * the variable references.
    */
  private object StringContextIntrinsic {
    def unapply(tree: Apply)(implicit ctx: Context): Option[(List[Literal], List[Tree])] = {
      tree match {
        case SOrRawInterpolator(strs, elems) =>
          if (tree.symbol == defn.StringContextRaw) Some(strs, elems)
          else { // tree.symbol == defn.StringContextS
            try {
              val escapedStrs = strs.map { str =>
                val escapedValue = StringContext.processEscapes(str.const.stringValue)
                cpy.Literal(str)(Constant(escapedValue))
              }
              Some(escapedStrs, elems)
            } catch {
              case _: StringContext.InvalidEscapeException => None
            }
          }
        case _ => None
      }
    }
  }

  override def transformApply(tree: Apply)(implicit ctx: Context): Tree = {
    val sym = tree.symbol
    val isInterpolatedMethod = // Test names first to avoid loading scala.StringContext if not used
      (sym.name == nme.raw_ && sym.eq(defn.StringContextRaw)) ||
      (sym.name == nme.s && sym.eq(defn.StringContextS))
    if (isInterpolatedMethod) transformInterpolator(tree)
    else tree
  }

  private def transformInterpolator(tree: Tree)(implicit ctx: Context): Tree = {
    tree match {
      case StringContextIntrinsic(strs: List[Literal], elems: List[Tree]) =>
        val stri = strs.iterator
        val elemi = elems.iterator
        var result: Tree = stri.next
        def concat(tree: Tree): Unit = {
          result = result.select(defn.String_+).appliedTo(tree)
        }
        while (elemi.hasNext) {
          concat(elemi.next)
          val str = stri.next
          if (!str.const.stringValue.isEmpty) concat(str)
        }
        result
      case _ => tree
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy