Skip to content

Rename QuoteContext to Quotes #10432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion community-build/community-projects/PPrint
2 changes: 1 addition & 1 deletion community-build/community-projects/dotty-cps-async
Submodule dotty-cps-async updated 29 files
+5 −5 src/main/scala/cps/Async.scala
+35 −35 src/main/scala/cps/CpsExpr.scala
+1 −1 src/main/scala/cps/forest/ApplyTransform.scala
+2 −2 src/main/scala/cps/forest/ApplyTreeTransform.scala
+4 −4 src/main/scala/cps/forest/AssignTransform.scala
+5 −5 src/main/scala/cps/forest/BlockTransform.scala
+2 −2 src/main/scala/cps/forest/ConstTransform.scala
+1 −1 src/main/scala/cps/forest/IdentTransform.scala
+1 −1 src/main/scala/cps/forest/IfTransform.scala
+1 −1 src/main/scala/cps/forest/ImportTransform.scala
+6 −6 src/main/scala/cps/forest/InlinedTransform.scala
+1 −1 src/main/scala/cps/forest/LambdaTreeTransform.scala
+1 −1 src/main/scala/cps/forest/MatchTreeTransform.scala
+1 −1 src/main/scala/cps/forest/NewTransform.scala
+1 −1 src/main/scala/cps/forest/ReturnTransform.scala
+1 −1 src/main/scala/cps/forest/SelectTreeTransform.scala
+1 −1 src/main/scala/cps/forest/SuperTransform.scala
+1 −1 src/main/scala/cps/forest/ThisTransform.scala
+1 −1 src/main/scala/cps/forest/ThrowTransform.scala
+5 −5 src/main/scala/cps/forest/TransformUtil.scala
+2 −2 src/main/scala/cps/forest/TreeTransformScope.scala
+1 −1 src/main/scala/cps/forest/TryTransform.scala
+1 −1 src/main/scala/cps/forest/TypeApplyTransform.scala
+1 −1 src/main/scala/cps/forest/TypeApplyTreeTransform.scala
+1 −1 src/main/scala/cps/forest/TypedTransform.scala
+13 −13 src/main/scala/cps/forest/ValDefTransform.scala
+1 −1 src/main/scala/cps/forest/WhileTransform.scala
+3 −3 src/test/scala/cps/A3.scala
+5 −5 src/test/scala/cps/cbs2/TestCBS2.scala
2 changes: 1 addition & 1 deletion community-build/community-projects/scalatest
Submodule scalatest updated 20 files
+1 −1 common-test.dotty/src/main/scala/org/scalatest/LineNumberHelper.scala
+1 −1 project/GenAnyVals.scala
+6 −6 project/GenFactoriesDotty.scala
+1 −1 scalactic.dotty/src/main/scala/org/scalactic/BooleanMacro.scala
+3 −3 scalactic.dotty/src/main/scala/org/scalactic/Requirements.scala
+1 −1 scalactic.dotty/src/main/scala/org/scalactic/Snapshots.scala
+6 −6 scalactic.dotty/src/main/scala/org/scalactic/anyvals/CompileTimeAssertions.scala
+1 −1 scalactic.dotty/src/main/scala/org/scalactic/anyvals/NumericStringMacro.scala
+1 −1 scalactic.dotty/src/main/scala/org/scalactic/anyvals/PercentageIntMacros.scala
+1 −1 scalactic.dotty/src/main/scala/org/scalactic/anyvals/RegexStringMacro.scala
+1 −1 scalactic.dotty/src/main/scala/org/scalactic/source/Position.scala
+1 −1 scalactic.dotty/src/main/scala/org/scalactic/source/TypeInfoMacro.scala
+1 −1 scalatest.dotty/src/main/scala/org/scalatest/Assertions.scala
+3 −3 scalatest.dotty/src/main/scala/org/scalatest/AssertionsMacro.scala
+15 −15 scalatest.dotty/src/main/scala/org/scalatest/CompileMacro.scala
+3 −3 scalatest.dotty/src/main/scala/org/scalatest/DiagrammedAssertionsMacro.scala
+2 −2 scalatest.dotty/src/main/scala/org/scalatest/diagrams/DiagramsMacro.scala
+1 −1 scalatest.dotty/src/main/scala/org/scalatest/expectations/ExpectationsMacro.scala
+4 −4 scalatest.dotty/src/main/scala/org/scalatest/matchers/MatchPatternMacro.scala
+15 −15 scalatest.dotty/src/main/scala/org/scalatest/matchers/TypeMatcherMacro.scala
2 changes: 1 addition & 1 deletion community-build/sbt-scalajs-sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.3.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.3.1")
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ class Definitions {
@tu lazy val QuotedExprClass: ClassSymbol = requiredClass("scala.quoted.Expr")
@tu lazy val QuotedExprModule: Symbol = QuotedExprClass.companionModule

@tu lazy val QuoteContextClass: ClassSymbol = requiredClass("scala.quoted.QuoteContext")
@tu lazy val QuotesClass: ClassSymbol = requiredClass("scala.quoted.Quotes")

@tu lazy val QuoteUnpicklerClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteUnpickler")
@tu lazy val QuoteUnpickler_unpickleExpr: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleExpr")
Expand Down
18 changes: 9 additions & 9 deletions compiler/src/dotty/tools/dotc/core/StagingContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ object StagingContext {
private val QuotationLevel = new Property.Key[Int]

/** A key to be used in a context property that tracks the quoteation stack.
* Stack containing the QuoteContext references recieved by the surrounding quotes.
* Stack containing the Quotes references recieved by the surrounding quotes.
*/
private val QuoteContextStack = new Property.Key[List[tpd.Tree]]
private val QuotesStack = new Property.Key[List[tpd.Tree]]

private val TaggedTypes = new Property.Key[PCPCheckAndHeal.QuoteTypeTags]

Expand All @@ -31,11 +31,11 @@ object StagingContext {
def quoteContext(using Context): Context =
ctx.fresh.setProperty(QuotationLevel, level + 1)

/** Context with an incremented quotation level and pushes a refecence to a QuoteContext on the quote context stack */
def pushQuoteContext(qctxRef: tpd.Tree)(using Context): Context =
val old = ctx.property(QuoteContextStack).getOrElse(List.empty)
/** Context with an incremented quotation level and pushes a refecence to a Quotes on the quote context stack */
def pushQuotes(qctxRef: tpd.Tree)(using Context): Context =
val old = ctx.property(QuotesStack).getOrElse(List.empty)
ctx.fresh.setProperty(QuotationLevel, level + 1)
.setProperty(QuoteContextStack, qctxRef :: old)
.setProperty(QuotesStack, qctxRef :: old)

/** Context with a decremented quotation level. */
def spliceContext(using Context): Context =
Expand All @@ -50,12 +50,12 @@ object StagingContext {
/** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty.
* The quotation stack could be empty if we are in a top level splice or an eroneous splice directly witin a top level splice.
*/
def popQuoteContext()(using Context): (Option[tpd.Tree], Context) =
def popQuotes()(using Context): (Option[tpd.Tree], Context) =
val ctx1 = ctx.fresh.setProperty(QuotationLevel, level - 1)
val head =
ctx.property(QuoteContextStack) match
ctx.property(QuotesStack) match
case Some(x :: xs) =>
ctx1.setProperty(QuoteContextStack, xs)
ctx1.setProperty(QuotesStack, xs)
Some(x)
case _ =>
None // Splice at level 0 or lower
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.core.tasty.TastyPrinter
import dotty.tools.io.File

import scala.quoted.runtime.impl.QuoteContextImpl
import scala.quoted.runtime.impl.QuotesImpl

/** Phase that prints the trees in all loaded compilation units.
*
Expand Down Expand Up @@ -45,7 +45,7 @@ class DecompilationPrinter extends Phase {
else {
val unitFile = unit.source.toString.replace("\\", "/").replace(".class", ".tasty")
out.println(s"/** Decompiled from $unitFile */")
out.println(QuoteContextImpl.showDecompiledTree(unit.tpdTree))
out.println(QuotesImpl.showDecompiledTree(unit.tpdTree))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import dotty.tools.dotc.core._
import dotty.tools.dotc.core.tasty.TastyHTMLPrinter
import dotty.tools.dotc.reporting._

import scala.quoted.runtime.impl.QuoteContextImpl
import scala.quoted.runtime.impl.QuotesImpl

/**
* Decompiler to be used with IDEs
Expand Down Expand Up @@ -35,7 +35,7 @@ class IDEDecompilerDriver(val settings: List[String]) extends dotc.Driver {
run.printSummary()
val unit = ctx.run.units.head

val decompiled = QuoteContextImpl.showDecompiledTree(unit.tpdTree)
val decompiled = QuotesImpl.showDecompiledTree(unit.tpdTree)
val tree = new TastyHTMLPrinter(unit.pickled.head._2()).showContents()

reporter.removeBufferedMessages.foreach(message => System.err.println(message))
Expand Down
20 changes: 10 additions & 10 deletions compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import dotty.tools.dotc.report

import scala.reflect.ClassTag

import scala.quoted.QuoteContext
import scala.quoted.Quotes
import scala.quoted.runtime.impl._

import scala.collection.mutable
Expand All @@ -41,19 +41,19 @@ object PickledQuotes {
/** Transform the expression into its fully spliced Tree */
def quotedExprToTree[T](expr: quoted.Expr[T])(using Context): Tree = {
val expr1 = expr.asInstanceOf[ExprImpl]
expr1.checkScopeId(QuoteContextImpl.scopeId)
expr1.checkScopeId(QuotesImpl.scopeId)
changeOwnerOfTree(expr1.tree, ctx.owner)
}

/** Transform the expression into its fully spliced TypeTree */
def quotedTypeToTree(tpe: quoted.Type[?])(using Context): Tree = {
val tpe1 = tpe.asInstanceOf[TypeImpl]
tpe1.checkScopeId(QuoteContextImpl.scopeId)
tpe1.checkScopeId(QuotesImpl.scopeId)
changeOwnerOfTree(tpe1.typeTree, ctx.owner)
}

/** Unpickle the tree contained in the TastyExpr */
def unpickleTerm(pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?])(using Context): Tree = {
def unpickleTerm(pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.Quotes) => scala.quoted.Expr[?])(using Context): Tree = {
val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = false))
val Inlined(call, Nil, expnasion) = unpickled
val inlineCtx = inlineContext(call)
Expand All @@ -63,22 +63,22 @@ object PickledQuotes {
}

/** Unpickle the tree contained in the TastyType */
def unpickleTypeTree(pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?])(using Context): Tree = {
def unpickleTypeTree(pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.Quotes) => scala.quoted.Expr[?])(using Context): Tree = {
val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = true))
spliceTypes(unpickled, typeHole, termHole)
}

/** Replace all term holes with the spliced terms */
private def spliceTerms(tree: Tree, typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?])(using Context): Tree = {
private def spliceTerms(tree: Tree, typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.Quotes) => scala.quoted.Expr[?])(using Context): Tree = {
val evaluateHoles = new TreeMap {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
case Hole(isTerm, idx, args) =>
val reifiedArgs = args.map { arg =>
if (arg.isTerm) (using qctx: QuoteContext) => new ExprImpl(arg, QuoteContextImpl.scopeId)
else new TypeImpl(arg, QuoteContextImpl.scopeId)
if (arg.isTerm) (using q: Quotes) => new ExprImpl(arg, QuotesImpl.scopeId)
else new TypeImpl(arg, QuotesImpl.scopeId)
}
if isTerm then
val quotedExpr = termHole(idx, reifiedArgs, QuoteContextImpl())
val quotedExpr = termHole(idx, reifiedArgs, QuotesImpl())
val filled = PickledQuotes.quotedExprToTree(quotedExpr)

// We need to make sure a hole is created with the source file of the surrounding context, even if
Expand Down Expand Up @@ -123,7 +123,7 @@ object PickledQuotes {
}

/** Replace all type holes generated with the spliced types */
private def spliceTypes(tree: Tree, typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Int], scala.quoted.QuoteContext) => Any)(using Context): Tree = {
private def spliceTypes(tree: Tree, typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Int], scala.quoted.Quotes) => Any)(using Context): Tree = {
tree match
case Block(stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) =>
val typeSpliceMap = (stat :: rest).iterator.map {
Expand Down
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import scala.annotation.constructorOnly
* typeHole = (idx: Int, args: List[Any]) => idx match {
* case 0 => ...
* },
* termHole = (idx: Int, args: List[Any], qctx: QuoteContext) => idx match {
* termHole = (idx: Int, args: List[Any], qctx: Quotes) => idx match {
* case 0 => ...
* ...
* case <i> =>
Expand Down Expand Up @@ -159,7 +159,7 @@ class PickleQuotes extends MacroTransform {
*/
def pickleAsLiteral(lit: Literal) = {
val exprType = defn.QuotedExprClass.typeRef.appliedTo(body.tpe)
val lambdaTpe = MethodType(defn.QuoteContextClass.typeRef :: Nil, exprType)
val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, exprType)
def mkConst(ts: List[Tree]) = {
val reflect = ts.head.select("reflect".toTermName)
val typeName = body.tpe.typeSymbol.name
Expand Down Expand Up @@ -239,7 +239,7 @@ class PickleQuotes extends MacroTransform {
else
Lambda(
MethodType(
List(defn.IntType, defn.SeqType.appliedTo(defn.AnyType), defn.QuoteContextClass.typeRef),
List(defn.IntType, defn.SeqType.appliedTo(defn.AnyType), defn.QuotesClass.typeRef),
defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType)),
args => {
val cases = termSplices.map { case (splice, idx) =>
Expand All @@ -253,7 +253,7 @@ class PickleQuotes extends MacroTransform {

val quoteClass = if isType then defn.QuotedTypeClass else defn.QuotedExprClass
val quotedType = quoteClass.typeRef.appliedTo(originalTp)
val lambdaTpe = MethodType(defn.QuoteContextClass.typeRef :: Nil, quotedType)
val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, quotedType)
def callUnpickle(ts: List[Tree]) = {
val qctx = ts.head.asInstance(defn.QuoteUnpicklerClass.typeRef)
val unpickleMeth = if isType then defn.QuoteUnpickler_unpickleType else defn.QuoteUnpickler_unpickleExpr
Expand All @@ -275,7 +275,7 @@ class PickleQuotes extends MacroTransform {
def taggedType() =
val typeType = defn.QuotedTypeClass.typeRef.appliedTo(body.tpe)
val classTree = TypeApply(ref(defn.Predef_classOf.termRef), body :: Nil)
val lambdaTpe = MethodType(defn.QuoteContextClass.typeRef :: Nil, typeType)
val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, typeType)
def callTypeConstructorOf(ts: List[Tree]) = {
val reflect = ts.head.select("reflect".toTermName)
val typeRepr = reflect.select("TypeRepr".toTermName).select("typeConstructorOf".toTermName).appliedTo(classTree)
Expand Down Expand Up @@ -370,7 +370,7 @@ class PickleQuotes extends MacroTransform {
assert(tpw.isInstanceOf[ValueType])
val argTpe =
if (tree.isType) defn.QuotedTypeClass.typeRef.appliedTo(tpw)
else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpw))
else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpw))
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).cast(argTpe)
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
i += 1
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import scala.reflect.ClassTag

import dotty.tools.dotc.quoted.{PickledQuotes, QuoteUtils}

import scala.quoted.QuoteContext
import scala.quoted.Quotes
import scala.quoted.runtime.impl._

/** Utility class to splice quoted expressions */
Expand All @@ -51,8 +51,8 @@ object Splicer {
val interpreter = new Interpreter(pos, classLoader)

// Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
val interpretedExpr = interpreter.interpret[QuoteContext => scala.quoted.Expr[Any]](tree)
val interpretedTree = interpretedExpr.fold(tree)(macroClosure => PickledQuotes.quotedExprToTree(macroClosure(QuoteContextImpl())))
val interpretedExpr = interpreter.interpret[Quotes => scala.quoted.Expr[Any]](tree)
val interpretedTree = interpretedExpr.fold(tree)(macroClosure => PickledQuotes.quotedExprToTree(macroClosure(QuotesImpl())))

checkEscapedVariables(interpretedTree, macroOwner)
} finally {
Expand Down Expand Up @@ -325,10 +325,10 @@ object Splicer {
}

private def interpretQuote(tree: Tree)(implicit env: Env): Object =
new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(tree, ctx.owner)).withSpan(tree.span), QuoteContextImpl.scopeId)
new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(tree, ctx.owner)).withSpan(tree.span), QuotesImpl.scopeId)

private def interpretTypeQuote(tree: Tree)(implicit env: Env): Object =
new TypeImpl(QuoteUtils.changeOwnerOfTree(tree, ctx.owner), QuoteContextImpl.scopeId)
new TypeImpl(QuoteUtils.changeOwnerOfTree(tree, ctx.owner), QuotesImpl.scopeId)

private def interpretLiteral(value: Any)(implicit env: Env): Object =
value.asInstanceOf[Object]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
try dropEmptyBlocks(quotedTree) match {
case Spliced(t) =>
// '{ $x } --> x
// and adapt the refinment of `QuoteContext { type tasty: ... } ?=> Expr[T]`
// and adapt the refinment of `Quotes { type tasty: ... } ?=> Expr[T]`
transform(t).asInstance(tree.tpe)
case _ => transformQuotation(quotedTree, tree)
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ trait QuotesAndSplices {
report.warning("Canceled splice directly inside a quote. '[ ${ XYZ } ] is equivalent to XYZ.", tree.srcPos)
case _ =>
}
val qctx = inferImplicitArg(defn.QuoteContextClass.typeRef, tree.span)
val qctx = inferImplicitArg(defn.QuotesClass.typeRef, tree.span)

if qctx.tpe.isInstanceOf[SearchFailureType] then
report.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span))
report.error(missingArgMsg(qctx, defn.QuotesClass.typeRef, ""), ctx.source.atSpan(tree.span))
else if !qctx.tpe.isStable then
report.error(em"Quotes require stable QuoteContext, but found non stable $qctx", qctx.srcPos)
report.error(em"Quotes require stable Quotes, but found non stable $qctx", qctx.srcPos)

val tree1 =
if ctx.mode.is(Mode.Pattern) then
Expand All @@ -65,7 +65,7 @@ trait QuotesAndSplices {
else report.warning(msg, tree.srcPos)
typedTypeApply(untpd.TypeApply(untpd.ref(defn.QuotedTypeModule_of.termRef), tree.quoted :: Nil), pt)(using quoteContext).select(nme.apply).appliedTo(qctx)
else
typedApply(untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted), pt)(using pushQuoteContext(qctx)).select(nme.apply).appliedTo(qctx)
typedApply(untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted), pt)(using pushQuotes(qctx)).select(nme.apply).appliedTo(qctx)
tree1.withSpan(tree.span)
}

Expand Down Expand Up @@ -103,7 +103,7 @@ trait QuotesAndSplices {
markAsMacro(ctx)
}

val (outerQctx, ctx1) = popQuoteContext()
val (outerQctx, ctx1) = popQuotes()

val internalSplice =
outerQctx match
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/scala/quoted/runtime/impl/Matcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ import scala.annotation.{Annotation, compileTimeOnly}
*/
object Matcher {

abstract class QuoteMatcher[QCtx <: QuoteContext & Singleton](val qctx: QCtx) {
abstract class QuoteMatcher[QCtx <: Quotes & Singleton](val qctx: QCtx) {

// TODO improve performance

Expand Down
Loading