Skip to content

Commit

Permalink
Added caching for trait writers
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg Zoller committed Oct 19, 2023
1 parent 0304f9e commit eed5e6d
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 207 deletions.
119 changes: 0 additions & 119 deletions src/main/scala/co.blocke.scalajack/ReflectUtil.scala

This file was deleted.

94 changes: 12 additions & 82 deletions src/main/scala/co.blocke.scalajack/json/JsonWriter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,10 @@ object JsonWriter:
sb.append(',')
()
}
case _ =>
).asInstanceOf[Expr[Unit]]
val stmts = hintStmt +: fieldFns.map { case (fn, field) =>
case _ => '{ () }
) // .asInstanceOf[Expr[Unit]]
val stmts = hintStmt :: fieldFns.map { case (fn, field) =>
// val stmts = fieldFns.map { case (fn, field) =>
'{
val fieldValue = ${
Select.unique('{ a }.asTerm, field.name).asExpr
Expand Down Expand Up @@ -120,87 +121,16 @@ object JsonWriter:
val typedName = Expr(rt.typedName)
val traitType = rt.expr
'{ (a: T, sb: StringBuilder, cfg: JsonConfig) =>
ReflectUtil.inTermsOf[T]($traitType, a, sb, cfg)
sb
val comboName = ($traitType.typedName.toString + "::" + a.getClass.getName).asInstanceOf[TypedName]
Option(refCache.get(comboName)) match
case Some(writerFn) =>
writerFn.asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder](a, sb, cfg)
case None =>
val writerFn = ReflectUtil.inTermsOf[T]($traitType, a, sb, cfg)
refCache.put(comboName, writerFn)
writerFn(a, sb, cfg)
}

// def inTermsOf[T](a: T, sb: StringBuilder, cfg: json.JsonConfig)
/*
case rt: TraitRef[?] =>
val typedName = Expr(rt.typedName)
val zf = (s: String) => println("HERE: " + s)
// val zz = liftFunction(zf)
'{
val traitFn = (a: T, sb: StringBuilder, cfg: JsonConfig) =>
sb.append('{')
sb.append(cfg.typeHint)
sb.append(':')
sb.append('"')
sb.append(a.getClass.getName)
sb.append('"')
val sbLen = sb.length
def f(g: String => String): String = g(a.getClass.getName)
val s = ReflectUtil.mcr(f)
println("S: " + s)
// ${
// val f = Expr((s: String) => println("HERE: " + s))
// '{ f(a.getClass.getName) }
// lazy val z = '{ foo() }
// println("HERE: " + z.value)
// Expr(Nil)
// }
// val q2 = quotes
// val traitFn: (String, Quotes) => Expr[List[?]] = ReflectUtil.makeTraitFn()
// traitFn(a.getClass.getName, q2)
/*
val inTermsOf = RType.inTermsOf[T](a.getClass).asInstanceOf[ScalaClassRType[T]]
type U = inTermsOf.T
${
import quotes.reflect.*
val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean]
val inTermsOfRef = ReflectOnType[U](quotes)(TypeRepr.of[U])(using seenBefore)
println("--3-- " + inTermsOfRef) // PROBLEM <-- This is a TypeSymbolRef, and should be a class
val fieldFns = inTermsOfRef
.asInstanceOf[ScalaClassRef[?]]
.fields
.map { f =>
f.fieldRef.refType match
case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f)
}
val stmts = fieldFns.map { case (fn, field) =>
'{
val fieldValue = ${
Select.unique('{ a }.asTerm, field.name).asExpr
}
if isOkToWrite(fieldValue, cfg) then
${
field.fieldRef.refType match
case '[t] =>
'{
sb.append(${ Expr(field.name) })
sb.append(':')
val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder]
fn2(fieldValue.asInstanceOf[t], sb, cfg)
sb.append(',')
}
}
}
}
Expr.ofList(Nil)
}
*/
if sbLen == sb.length then sb.append('}')
else sb.setCharAt(sb.length() - 1, '}')
refCache.put($typedName, traitFn)
traitFn
}
*/

case rt: SeqRef[?] =>
rt.elementRef.refType match
case '[t] =>
Expand Down
53 changes: 53 additions & 0 deletions src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package co.blocke.scalajack
package json

import co.blocke.scala_reflection.{ReflectException, RType, RTypeRef, TypedName}
import co.blocke.scala_reflection.rtypes.TraitRType
import co.blocke.scala_reflection.reflect.rtypeRefs.*
import co.blocke.scala_reflection.reflect.*
import scala.quoted.*
import scala.quoted.staging.*

object ReflectUtil:

/**
* This function takes the RType of a trait and an instance of T and does two things.
* First it expresses the instance's class *in terms of* the trait's concrete type parameters (if any).
* Then it generates a writer function for the now correctly-typed class.
*
* @param traitType
* @param a
* @param sb
* @param cfg
* @return
*/
def inTermsOf[T](traitType: RType[?], a: T, sb: StringBuilder, cfg: JsonConfig) =
given Compiler = Compiler.make(getClass.getClassLoader)

val clazz = a.getClass

val fn = (quotes: Quotes) ?=> {
import quotes.reflect.*

val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]]

val inTermsOfRef = traitType match
case ttype: TraitRType[?] if ttype.typeParamSymbols.nonEmpty =>
val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean]
val paths = classRef.typePaths.getOrElse(ttype.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${ttype.name}"))
ttype.toType(quotes) match
case '[t] =>
val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t])
val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz)
ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore)

case traitRef: TraitRType[?] => classRef
case x => throw new ReflectException(s"${x.name} is not of type trait")

inTermsOfRef.refType match
case '[t] =>
val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[t]].copy(renderHint = true)
JsonWriter.writeJsonFn[t](asClassRef.asInstanceOf[RTypeRef[t]])
}
val writeFn = run(fn)
writeFn.asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder]
18 changes: 12 additions & 6 deletions src/main/scala/co.blocke.scalajack/run/Play.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ object RunMe extends App:
val p = Person("Greg", 57, List(false, true, true), Colors.Blue, "Fred".asInstanceOf[BigName])

val d = Dog("Fido", 4, 2, Some(Dog("Mindy", 4, 0, None)))
val d2 = Dog("Spot", 4, 3, Some(Dog("Floppy", 3, 1, None)))

val t0 = System.currentTimeMillis()
val cfg = json.JsonConfig()
// for i <- 0 to 10000 do
// val x = Codec.write(p)(using cfg)
// if i % 100 == 0 then println(i)
// if i == 10000 then println(x)

println(Codec.write(d)(using cfg))
val t0 = System.currentTimeMillis()
for i <- 0 to 10000 do
Codec.write(d)(using cfg)
if i % 100 == 0 then println(i)
// if i == 10000 then println(i)

// println(Codec.write(d)(using cfg))
// println("")
// println(Codec.write(d2)(using cfg))
// println("")
// println(Codec.write(d)(using cfg))

val t1 = System.currentTimeMillis()
println("TIME: " + (t1 - t0))

0 comments on commit eed5e6d

Please sign in to comment.