Skip to content

Commit 75f2365

Browse files
committed
More efficient codec derivation for generic and named tuples + code clean up
1 parent 9790835 commit 75f2365

File tree

2 files changed

+41
-51
lines changed

2 files changed

+41
-51
lines changed

jsoniter-scala-macros/shared/src/main/scala-2/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -752,8 +752,6 @@ object JsonCodecMaker {
752752

753753
val isScala213: Boolean = util.Properties.versionNumberString.startsWith("2.13.")
754754
val rootTpe = weakTypeOf[A].dealias
755-
val inferredKeyCodecs = mutable.Map.empty[Type, Tree]
756-
val inferredValueCodecs = mutable.Map.empty[Type, Tree]
757755

758756
def isImmutableArraySeq(tpe: Type): Boolean =
759757
isScala213 && tpe.typeSymbol.fullName == "scala.collection.immutable.ArraySeq"
@@ -780,6 +778,8 @@ object JsonCodecMaker {
780778
}
781779
}
782780

781+
val inferredKeyCodecs = mutable.Map.empty[Type, Tree]
782+
783783
def findImplicitKeyCodec(types: List[Type]): Tree = {
784784
checkRecursionInTypes(types)
785785
val tpe = types.head
@@ -790,6 +790,8 @@ object JsonCodecMaker {
790790
}
791791
}
792792

793+
val inferredValueCodecs = mutable.Map.empty[Type, Tree]
794+
793795
def findImplicitValueCodec(types: List[Type]): Tree = {
794796
checkRecursionInTypes(types)
795797
val tpe = types.head
@@ -1560,7 +1562,7 @@ object JsonCodecMaker {
15601562
if (readFields.size <= 8 && readFields.foldLeft(0)(_ + _.mappedName.length) <= 64) {
15611563
genReadCollisions(readFields)
15621564
} else {
1563-
val hashCode = (fieldInfo: FieldInfo) => JsonReader.toHashCode(fieldInfo.mappedName.toCharArray, fieldInfo.mappedName.length)
1565+
val hashCode = (fi: FieldInfo) => JsonReader.toHashCode(fi.mappedName.toCharArray, fi.mappedName.length)
15641566
val cases = groupByOrdered(readFields)(hashCode).map { case (hash, fs) =>
15651567
cq"$hash => ${genReadCollisions(fs)}"
15661568
} :+ cq"_ => $unexpectedFieldHandler"

jsoniter-scala-macros/shared/src/main/scala-3/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import scala.collection.mutable.ArrayBuffer
1414
import scala.language.implicitConversions
1515
import scala.quoted._
1616
import scala.reflect.ClassTag
17+
import scala.runtime.TupleXXL
1718

1819
@field
1920
final class named(val name: String) extends StaticAnnotation
@@ -790,11 +791,9 @@ object JsonCodecMaker {
790791
def toTuple(typeArgs: List[TypeRepr]): TypeRepr = {
791792
val size = typeArgs.size
792793
if (size > 0 && size <= 22) defn.TupleClass(size).typeRef.appliedTo(typeArgs)
793-
else {
794-
typeArgs.foldRight(TypeRepr.of[EmptyTuple]) {
795-
val tupleCons = TypeRepr.of[*:]
796-
(curr, acc) => tupleCons.appliedTo(List(curr, acc))
797-
}
794+
else typeArgs.foldRight(TypeRepr.of[EmptyTuple]) {
795+
val tupleCons = TypeRepr.of[*:]
796+
(curr, acc) => tupleCons.appliedTo(List(curr, acc))
798797
}
799798
}
800799

@@ -865,17 +864,19 @@ object JsonCodecMaker {
865864
Apply(TypeApply(Select(New(TypeIdent(defn.ArrayClass)), defn.ArrayClass.primaryConstructor), List(TypeTree.of[T])),
866865
List(size.asTerm)).asExprOf[Array[T]]
867866

867+
def symbol(name: String, tpe: TypeRepr, flags: Flags = Flags.EmptyFlags): Symbol =
868+
Symbol.newVal(Symbol.spliceOwner, name, tpe, flags, Symbol.noSymbol)
869+
868870
val rootTpe = TypeRepr.of[A].dealias
869871
val inferredOrderings = mutable.Map.empty[TypeRepr, Term]
870-
val inferredKeyCodecs = mutable.Map.empty[TypeRepr, Option[Expr[JsonKeyCodec[?]]]]
871-
val inferredValueCodecs = mutable.Map.empty[TypeRepr, Option[Expr[JsonValueCodec[?]]]]
872-
val classTags = mutable.Map.empty[TypeRepr, ValDef]
873872

874873
def summonOrdering(tpe: TypeRepr): Term = inferredOrderings.getOrElseUpdate(tpe, {
875874
tpe.asType match
876875
case '[t] => Expr.summon[Ordering[t]].fold(fail(s"Can't summon Ordering[${tpe.show}]"))(_.asTerm)
877876
})
878877

878+
val classTags = mutable.Map.empty[TypeRepr, ValDef]
879+
879880
def summonClassTag(tpe: TypeRepr): Term = Ref(classTags.getOrElseUpdate(tpe, {
880881
tpe.asType match
881882
case '[t] =>
@@ -888,9 +889,6 @@ object JsonCodecMaker {
888889
case v: ImplicitSearchSuccess => Some(v.tree.asExprOf[T])
889890
case _ => None
890891

891-
def symbol(name: String, tpe: TypeRepr, flags: Flags = Flags.EmptyFlags): Symbol =
892-
Symbol.newVal(Symbol.spliceOwner, name, tpe, flags, Symbol.noSymbol)
893-
894892
def checkRecursionInTypes(types: List[TypeRepr]): Unit =
895893
if (!cfg.allowRecursiveTypes) {
896894
val tpe = types.head
@@ -905,6 +903,8 @@ object JsonCodecMaker {
905903
}
906904
}
907905

906+
val inferredKeyCodecs = mutable.Map.empty[TypeRepr, Option[Expr[JsonKeyCodec[?]]]]
907+
908908
def findImplicitKeyCodec(types: List[TypeRepr]): Option[Expr[JsonKeyCodec[?]]] =
909909
checkRecursionInTypes(types)
910910
val tpe = types.head
@@ -913,6 +913,8 @@ object JsonCodecMaker {
913913
inferImplicitValue[JsonKeyCodec[?]](TypeRepr.of[JsonKeyCodec].appliedTo(tpe))
914914
})
915915

916+
val inferredValueCodecs = mutable.Map.empty[TypeRepr, Option[Expr[JsonValueCodec[?]]]]
917+
916918
def findImplicitValueCodec(types: List[TypeRepr]): Option[Expr[JsonValueCodec[?]]] =
917919
checkRecursionInTypes(types)
918920
val tpe = types.head
@@ -1001,18 +1003,17 @@ object JsonCodecMaker {
10011003
if (typeArgs.isEmpty) Expr(EmptyTuple).asTerm
10021004
else {
10031005
val arraySym = symbol("as", TypeRepr.of[Array[Any]])
1004-
val arrayRef = Ref(arraySym).asExprOf[Array[Any]]
10051006
val arrayValDef = ValDef(arraySym, Some('{ new Array[Any](${Expr(typeArgs.size)}) }.asTerm))
1007+
val arrayUpdate = Select.unique(Ref(arraySym), "update")
10061008
val assignments = args.map {
10071009
var i = -1
10081010
term =>
10091011
i += 1
1010-
'{ $arrayRef(${Expr(i)}) = ${term.asExprOf[Any]} }.asTerm
1012+
Apply(arrayUpdate, List(Literal(IntConstant(i)), term))
10111013
}
1012-
val block = Block(arrayValDef :: assignments, arrayRef.asTerm).asExprOf[Array[Any]]
1014+
val block = Block(arrayValDef :: assignments, Ref(arraySym)).asExprOf[Array[Any]]
10131015
tupleType match
1014-
case '[tt] =>
1015-
'{ scala.runtime.TupleXXL.fromIArray($block.asInstanceOf[IArray[Object]]).asInstanceOf[tt] }.asTerm
1016+
case '[tt] => '{ TupleXXL.fromIArray($block.asInstanceOf[IArray[Object]]).asInstanceOf[tt] }.asTerm
10161017
}
10171018
} else {
10181019
val constructorNoTypes = Select(New(Inferred(tupleTpe)), tupleTpe.typeSymbol.primaryConstructor)
@@ -1030,12 +1031,11 @@ object JsonCodecMaker {
10301031
val names = tupleTypeArgs(nTpe.dealias.asType).map { case ConstantType(StringConstant(name)) => name }
10311032
val typeArgs = tupleTypeArgs(tTpe.dealias.asType)
10321033
val tupleTpe = toTuple(typeArgs)
1033-
val noSymbol = Symbol.noSymbol
10341034
var i = - 1
10351035
NamedTupleInfo(tpe, tupleTpe, typeArgs, List(names.zip(typeArgs).map { case (name, fTpe) =>
10361036
i += 1
10371037
val mappedName = cfg.fieldNameMapper(name).getOrElse(name)
1038-
FieldInfo(noSymbol, mappedName, noSymbol, None, fTpe, false, false, i)
1038+
FieldInfo(Symbol.noSymbol, mappedName, Symbol.noSymbol, None, fTpe, false, false, i)
10391039
}))
10401040
case _ => fail(s"Unexpected named type: ${tpe.show}")
10411041
}
@@ -2167,8 +2167,7 @@ object JsonCodecMaker {
21672167
}
21682168
${Block(checkReqVars, construct).asExprOf[T]}
21692169
}.asTerm)
2170-
If('{ $in.isNextToken('{') }.asTerm, readNonEmpty,
2171-
'{ $in.readNullOrTokenError($default, '{') }.asTerm).asExprOf[T]
2170+
If('{ $in.isNextToken('{') }.asTerm, readNonEmpty, '{ $in.readNullOrTokenError($default, '{') }.asTerm).asExprOf[T]
21722171
}
21732172

21742173
def genReadConstType[T: Type](tpe: TypeRepr, isStringified: Boolean, in: Expr[JsonReader])(using Quotes): Expr[T] = tpe match
@@ -2444,8 +2443,7 @@ object JsonCodecMaker {
24442443
x => '{ $x.update($readKey, { if ($in.isNextToken(',')) $readVal else $in.commaError() }) },
24452444
identity, in, tDefault).asExprOf[T]
24462445
} else {
2447-
genReadMap(newBuilder,
2448-
x => '{ $x.update($in.readKeyAsLong(), $readVal) }, identity, in, tDefault).asExprOf[T]
2446+
genReadMap(newBuilder, x => '{ $x.update($in.readKeyAsLong(), $readVal) }, identity, in, tDefault).asExprOf[T]
24492447
}
24502448
} else if (tpe <:< TypeRepr.of[immutable.LongMap[?]]) withDecoderFor(methodKey, default, in) { (in, default) =>
24512449
val tpe1 = typeArg1(tpe)
@@ -2480,8 +2478,7 @@ object JsonCodecMaker {
24802478
else $tEmpty
24812479
}.asExprOf[T & mutable.Map[t1, t2]]
24822480

2483-
def readVal2(using Quotes) =
2484-
genReadVal(tpe2 :: types, genNullValue[t2](tpe2 :: types), isStringified, false, in)
2481+
def readVal2(using Quotes) = genReadVal(tpe2 :: types, genNullValue[t2](tpe2 :: types), isStringified, false, in)
24852482

24862483
if (cfg.mapAsArray) {
24872484
val readVal1 = genReadVal(tpe1 :: types, genNullValue[t1](tpe1 :: types), isStringified, false, in)
@@ -2500,24 +2497,22 @@ object JsonCodecMaker {
25002497
def builderNoApply =
25012498
TypeApply(Select.unique(scalaCollectionCompanion(tpe), "newBuilder"), List(TypeTree.of[t1], TypeTree.of[t2]))
25022499

2500+
def readKey(using Quotes) = genReadKey[t1](tpe1 :: types, in)
2501+
2502+
def readVal2(using Quotes) = genReadVal(tpe2 :: types, genNullValue[t2](tpe2 :: types), isStringified, false, in)
2503+
2504+
def readVal1(using Quotes) = genReadVal(tpe1 :: types, genNullValue[t1](tpe1 :: types), isStringified, false, in)
2505+
25032506
val newBuilder =
25042507
(if (tpe <:< TypeRepr.of[collection.SortedMap[?, ?]]) Apply(builderNoApply, List(summonOrdering(tpe1)))
25052508
else if (tpe <:< TypeRepr.of[immutable.TreeSeqMap[?, ?]]) '{ immutable.TreeSeqMap.newBuilder[t1, t2] }.asTerm
25062509
else builderNoApply).asExprOf[mutable.Builder[(t1, t2), T & collection.Map[t1, t2]]]
25072510

2508-
def readVal2(using Quotes) =
2509-
genReadVal(tpe2 :: types, genNullValue[t2](tpe2 :: types), isStringified, false, in)
2510-
25112511
if (cfg.mapAsArray) {
2512-
def readVal1(using Quotes) =
2513-
genReadVal(tpe1 :: types, genNullValue[t1](tpe1 :: types), isStringified, false, in)
2514-
25152512
genReadMapAsArray(newBuilder,
25162513
x => '{ $x.addOne(new Tuple2($readVal1, { if ($in.isNextToken(',')) $readVal2 else $in.commaError() })): Unit},
25172514
x => '{ $x.result() }, in, default).asExprOf[T]
25182515
} else {
2519-
def readKey(using Quotes) = genReadKey[t1](tpe1 :: types, in)
2520-
25212516
genReadMap(newBuilder, x => '{ $x.addOne(new Tuple2($readKey, $readVal2)): Unit },
25222517
x => '{ $x.result() }, in, default).asExprOf[T]
25232518
}
@@ -2720,16 +2715,16 @@ object JsonCodecMaker {
27202715
if (size == 0) Expr(EmptyTuple)
27212716
else if (size > 22) {
27222717
val arraySym = symbol("as", TypeRepr.of[Array[Any]])
2723-
val arrayRef = Ref(arraySym).asExprOf[Array[Any]]
27242718
val arrayValDef = ValDef(arraySym, Some('{ new Array[Any](${Expr(size)}) }.asTerm))
2719+
val arrayUpdate = Select.unique(Ref(arraySym), "update")
27252720
val assignments = valDefs.map {
27262721
var i = - 1
27272722
valDef =>
27282723
i += 1
2729-
'{ $arrayRef(${Expr(i)}) = ${Ref(valDef.symbol).asExprOf[Any]} }.asTerm
2724+
Apply(arrayUpdate, List(Literal(IntConstant(i)), Ref(valDef.symbol)))
27302725
}
2731-
val block = Block(arrayValDef :: assignments, arrayRef.asTerm).asExprOf[Array[Any]]
2732-
'{ scala.runtime.TupleXXL.fromIArray($block.asInstanceOf[IArray[Object]]).asInstanceOf[T] }
2726+
val block = Block(arrayValDef :: assignments, Ref(arraySym)).asExprOf[Array[Any]]
2727+
'{ TupleXXL.fromIArray($block.asInstanceOf[IArray[Object]]).asInstanceOf[T] }
27332728
} else {
27342729
val constructorNoTypes = Select(New(Inferred(tTpe)), tTpe.typeSymbol.primaryConstructor)
27352730
Apply(TypeApply(constructorNoTypes, indexedTypes.map(Inferred(_))), valDefs.map(x => Ref(x.symbol))).asExpr
@@ -2763,18 +2758,11 @@ object JsonCodecMaker {
27632758
val (valDefs, valRef) =
27642759
typeInfo match
27652760
case namedTupleInfo: NamedTupleInfo =>
2766-
val valDef = ValDef(
2767-
Symbol.newVal(Symbol.spliceOwner, "t", namedTupleInfo.tupleTpe, Flags.EmptyFlags, Symbol.noSymbol),
2768-
Some(
2769-
Apply(
2770-
Select
2771-
.unique(Ref(Symbol.requiredModule("scala.NamedTuple")), "toTuple")
2772-
.appliedToTypeTrees(tpe.typeArgs.map(_.asType match { case '[t] => TypeTree.of[t] })),
2773-
List(x.asTerm)
2774-
)
2775-
)
2776-
)
2777-
(List(valDef), Ref(valDef.symbol))
2761+
val sym = symbol("t", namedTupleInfo.tupleTpe)
2762+
val toTupleMethod =
2763+
Select.unique(Ref(Symbol.requiredModule("scala.NamedTuple")), "toTuple").appliedToTypes(tpe.typeArgs)
2764+
val valDef = ValDef(sym, Some(Apply(toTupleMethod, List(x.asTerm))))
2765+
(List(valDef), Ref(sym))
27782766
case _ => (Nil, x.asTerm)
27792767
val writeFields = typeInfo.fields.map { fieldInfo =>
27802768
val fDefault =

0 commit comments

Comments
 (0)