Skip to content

Commit

Permalink
fix derived JsonWriter performance
Browse files Browse the repository at this point in the history
  • Loading branch information
goshacodes committed Jul 11, 2024
1 parent 127f2c6 commit c6b7731
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 37 deletions.
63 changes: 39 additions & 24 deletions modules/core/src/main/scala-3/tethys/derivation/Derivation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package tethys.derivation
import tethys.writers.tokens.TokenWriter
import tethys.readers.FieldName
import tethys.readers.ReaderError
import tethys.{JsonConfig, JsonReader, JsonWriter, WriterBuilder, ReaderBuilder}
import tethys.{JsonConfig, JsonReader, JsonWriter, JsonObjectWriter, WriterBuilder, ReaderBuilder}

import scala.compiletime.{constValueTuple, summonInline}

Expand All @@ -12,9 +12,9 @@ object Derivation:

inline def fieldJsonWriter[T, F]: JsonWriter[F] =
${ DerivationMacro.fieldJsonWriter[T, F] }

inline def parseWriterBuilder[T](inline config: WriterBuilder[T]): WriterBuilderParsed[T] =
${ DerivationMacro.parseWriterBuilder[T]('{config}) }
inline def deriveJsonWriterForProduct[T](inline config: WriterBuilder[T]): JsonObjectWriter[T] =
${ DerivationMacro.deriveJsonWriterForProduct[T]('{config})}

inline def parseReaderBuilder[T](inline config: ReaderBuilder[T]): ReaderBuilderParsed =
${ DerivationMacro.parseReaderBuilder[T]('{config})}
Expand All @@ -30,28 +30,43 @@ object Derivation:
private[derivation]
object DerivationMacro:
import scala.quoted.*

def parseWriterBuilder[T: Type](config: Expr[WriterBuilder[T]])(using quotes: Quotes): Expr[WriterBuilderParsed[T]] =
def deriveJsonWriterForProduct[T: Type](config: Expr[WriterBuilder[T]])(using quotes: Quotes): Expr[JsonObjectWriter[T]] =
val utils = new ConfigurationMacroUtils
import utils.quotes.reflect.*
'{
WriterBuilderParsed(
${
Expr.ofList(
utils.prepareWriterProductFields[T](config).map { field =>
field.tpe.asType match
case '[fieldType] => '{
WriterBuilderParsed.Field(
label = ${ field.label },
function = (value: T) => ${ field.value('{ value }.asTerm).asExprOf[fieldType] },
writer = Derivation.fieldJsonWriter[T, fieldType]
)
}
}

val fields = utils.prepareWriterProductFields(config)

def allocateWriters(types: List[TypeRepr]): (List[Statement], List[Ref]) = types.zipWithIndex.map { (tpe, idx) =>
tpe.asType match
case '[fieldType] =>
val term = ValDef(
Symbol.newVal(Symbol.spliceOwner, s"jsonWriter${tpe.typeSymbol.name}$idx", TypeRepr.of[JsonWriter[fieldType]], Flags.Lazy, Symbol.noSymbol),
Some('{ Derivation.fieldJsonWriter[T, fieldType] }.asTerm)
)
}
)
}
(term, Ref(term.symbol))
}.unzip

val (writersStatements, writersRefs) = allocateWriters(fields.map(_.tpe))
Block(
writersStatements,
'{
new JsonObjectWriter[T]:
override def writeValues(value: T, tokenWriter: TokenWriter): Unit =
$ {
Expr.block(
fields.zip(writersRefs).map { (field, writer) =>
field.tpe.asType match
case '[f] =>
'{ ${ writer.asExprOf[JsonWriter[f]] }
.write(${ field.label }, ${ field.value('{ value }.asTerm).asExprOf[f] }, tokenWriter) }
},
'{}
)
}
}.asTerm
).asExprOf[JsonObjectWriter[T]]


def fieldJsonWriter[T: Type, F: Type](using quotes: Quotes): Expr[JsonWriter[F]] =
val utils = new ConfigurationMacroUtils
Expand All @@ -62,7 +77,7 @@ object DerivationMacro:
tpe.asType match
case '[fieldType] =>
val term = ValDef(
Symbol.newVal(Symbol.spliceOwner, s"jsonWriter$idx", TypeRepr.of[JsonWriter[fieldType]], Flags.Lazy, Symbol.noSymbol),
Symbol.newVal(Symbol.spliceOwner, s"jsonWriter${tpe.typeSymbol.name}$idx", TypeRepr.of[JsonWriter[fieldType]], Flags.Lazy, Symbol.noSymbol),
Some('{ JsonObjectWriterDerivation.summonOrDeriveJsonWriterForField[T, fieldType] }.asTerm)
)
(term, Ref(term.symbol))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@ import scala.compiletime.{constValueTuple, erasedValue, summonFrom, summonInline
private[tethys] trait JsonObjectWriterDerivation:

inline def derived[A](inline config: WriterBuilder[A])(using mirror: Mirror.ProductOf[A]) =
new JsonObjectWriter[A]:
lazy val configuration: WriterBuilderParsed[A] = Derivation.parseWriterBuilder[A](config)

override def writeValues(value: A, tokenWriter: TokenWriter): Unit =
configuration.fields.foreach { field =>
field.writer.write(field.label, field.function(value), tokenWriter)
}
Derivation.deriveJsonWriterForProduct[A](config)

inline def derived[A](inline config: JsonConfig[A])(using m: Mirror.SumOf[A]) =
new JsonObjectWriter[A]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ package tethys.derivation

import tethys.JsonWriter

private[derivation]
case class WriterBuilderParsed[T](
fields: List[WriterBuilderParsed.Field[T, ?]]
)


private[derivation]
object WriterBuilderParsed:
case class Field[T, F](label: String, function: T => F, writer: JsonWriter[F])

0 comments on commit c6b7731

Please sign in to comment.