From c6b773105f554c34adc1ebde49fba4767c5146e4 Mon Sep 17 00:00:00 2001 From: Gosha Kovalyov Date: Thu, 11 Jul 2024 12:50:34 +0500 Subject: [PATCH] fix derived JsonWriter performance --- .../tethys/derivation/Derivation.scala | 63 ++++++++++++------- .../JsonObjectWriterDerivation.scala | 8 +-- .../derivation/WriterBuilderParsed.scala | 6 -- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/modules/core/src/main/scala-3/tethys/derivation/Derivation.scala b/modules/core/src/main/scala-3/tethys/derivation/Derivation.scala index cacf9c3..7459fe7 100644 --- a/modules/core/src/main/scala-3/tethys/derivation/Derivation.scala +++ b/modules/core/src/main/scala-3/tethys/derivation/Derivation.scala @@ -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} @@ -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})} @@ -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 @@ -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)) diff --git a/modules/core/src/main/scala-3/tethys/derivation/JsonObjectWriterDerivation.scala b/modules/core/src/main/scala-3/tethys/derivation/JsonObjectWriterDerivation.scala index dfde516..5dade9d 100644 --- a/modules/core/src/main/scala-3/tethys/derivation/JsonObjectWriterDerivation.scala +++ b/modules/core/src/main/scala-3/tethys/derivation/JsonObjectWriterDerivation.scala @@ -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]: diff --git a/modules/core/src/main/scala-3/tethys/derivation/WriterBuilderParsed.scala b/modules/core/src/main/scala-3/tethys/derivation/WriterBuilderParsed.scala index 3736563..faa9f54 100644 --- a/modules/core/src/main/scala-3/tethys/derivation/WriterBuilderParsed.scala +++ b/modules/core/src/main/scala-3/tethys/derivation/WriterBuilderParsed.scala @@ -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]) \ No newline at end of file