Skip to content

Commit dc7e6bf

Browse files
committed
Faster derivation of product types
1 parent e948fb9 commit dc7e6bf

File tree

2 files changed

+55
-48
lines changed

2 files changed

+55
-48
lines changed

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

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -828,42 +828,49 @@ object JsonCodecMaker {
828828
def getClassInfo(tpe: Type): ClassInfo = classInfos.getOrElseUpdate(tpe, {
829829
case class FieldAnnotations(partiallyMappedName: String, transient: Boolean, stringified: Boolean)
830830

831-
def getPrimaryConstructor(tpe: Type): MethodSymbol = tpe.decls.collectFirst {
832-
case m: MethodSymbol if m.isPrimaryConstructor => m // FIXME: sometime it cannot be accessed from the place of the `make` call
833-
}.getOrElse(fail(s"Cannot find a primary constructor for '$tpe'"))
834-
835831
def hasSupportedAnnotation(m: TermSymbol): Boolean = {
836832
m.info: Unit // to enforce the type information completeness and availability of annotations
837-
m.annotations.exists(a => a.tree.tpe =:= typeOf[named] || a.tree.tpe =:= typeOf[transient] ||
838-
a.tree.tpe =:= typeOf[stringified] || (cfg.scalaTransientSupport && a.tree.tpe =:= typeOf[scala.transient]))
833+
m.annotations.exists { a =>
834+
val tpe = a.tree.tpe
835+
tpe =:= typeOf[named] || tpe =:= typeOf[transient] || tpe =:= typeOf[stringified] ||
836+
(cfg.scalaTransientSupport && tpe =:= typeOf[scala.transient])
837+
}
839838
}
840839

841840
def supportedTransientTypeNames: String =
842841
if (cfg.scalaTransientSupport) s"'${typeOf[transient]}' (or '${typeOf[scala.transient]}')"
843842
else s"'${typeOf[transient]}')"
844843

845844
lazy val module = companion(tpe).asModule // don't lookup for the companion when there are no default values for constructor params
846-
val getters = tpe.members.collect { case m: MethodSymbol if m.isParamAccessor && m.isGetter => m }
847-
val annotations = tpe.members.collect {
845+
var getters = Map.empty[String, MethodSymbol]
846+
tpe.members.foreach {
847+
case m: MethodSymbol if m.isParamAccessor && m.isGetter => getters = getters.updated(decodeName(m), m)
848+
case _ =>
849+
}
850+
var annotations = Map.empty[String, FieldAnnotations]
851+
tpe.members.foreach {
848852
case m: TermSymbol if hasSupportedAnnotation(m) =>
849853
val name = decodeFieldName(m)
850-
val named = m.annotations.filter(_.tree.tpe =:= typeOf[named])
851-
if (named.size > 1) fail(s"Duplicated '${typeOf[named]}' defined for '$name' of '$tpe'.")
852-
val trans = m.annotations.filter(a => a.tree.tpe =:= typeOf[transient] ||
854+
val named = m.annotations.count(_.tree.tpe =:= typeOf[named])
855+
if (named > 1) fail(s"Duplicated '${typeOf[named]}' defined for '$name' of '$tpe'.")
856+
val trans = m.annotations.count(a => a.tree.tpe =:= typeOf[transient] ||
853857
(cfg.scalaTransientSupport && a.tree.tpe =:= typeOf[scala.transient]))
854-
if (trans.size > 1) warn(s"Duplicated $supportedTransientTypeNames defined for '$name' of '$tpe'.")
855-
val strings = m.annotations.filter(_.tree.tpe =:= typeOf[stringified])
856-
if (strings.size > 1) warn(s"Duplicated '${typeOf[stringified]}' defined for '$name' of '$tpe'.")
857-
if ((named.nonEmpty || strings.nonEmpty) && trans.nonEmpty) {
858+
if (trans > 1) warn(s"Duplicated $supportedTransientTypeNames defined for '$name' of '$tpe'.")
859+
val strings = m.annotations.count(_.tree.tpe =:= typeOf[stringified])
860+
if (strings > 1) warn(s"Duplicated '${typeOf[stringified]}' defined for '$name' of '$tpe'.")
861+
if ((named > 0 || strings > 0) && trans > 0) {
858862
warn(s"Both $supportedTransientTypeNames and '${typeOf[named]}' or " +
859863
s"$supportedTransientTypeNames and '${typeOf[stringified]}' defined for '$name' of '$tpe'.")
860864
}
861-
val partiallyMappedName = namedValueOpt(named.headOption, tpe).getOrElse(name)
862-
(name, FieldAnnotations(partiallyMappedName, trans.nonEmpty, strings.nonEmpty))
863-
}.toMap
865+
val partiallyMappedName = namedValueOpt(m.annotations.find(_.tree.tpe =:= typeOf[named]), tpe).getOrElse(name)
866+
annotations = annotations.updated(name, FieldAnnotations(partiallyMappedName, trans > 0, strings > 0))
867+
case _ =>
868+
}
864869
ClassInfo(tpe, {
865870
var i = 0
866-
getPrimaryConstructor(tpe).paramLists.map(_.flatMap { p =>
871+
tpe.decls.collectFirst {
872+
case m: MethodSymbol if m.isPrimaryConstructor => m // FIXME: sometime it cannot be accessed from the place of the `make` call
873+
}.getOrElse(fail(s"Cannot find a primary constructor for '$tpe'")).paramLists.map(_.flatMap { p =>
867874
i += 1
868875
val symbol = p.asTerm
869876
val name = decodeName(symbol)
@@ -873,9 +880,8 @@ object JsonCodecMaker {
873880
val fieldNameMapper: String => String = n => cfg.fieldNameMapper.lift(n).getOrElse(n)
874881
val mappedName = annotationOption.fold(fieldNameMapper(name))(_.partiallyMappedName)
875882
val tmpName = TermName("_" + symbol.name)
876-
val getter = getters.find(_.name == symbol.name).getOrElse {
877-
fail(s"'$name' parameter of '$tpe' should be defined as 'val' or 'var' in the primary constructor.")
878-
}
883+
val getter = getters.getOrElse(name,
884+
fail(s"'$name' parameter of '$tpe' should be defined as 'val' or 'var' in the primary constructor."))
879885
val defaultValue =
880886
if (!cfg.requireDefaultFields && symbol.isParamWithDefault) {
881887
Some(q"$module.${TermName("$lessinit$greater$default$" + i)}")

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

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -887,35 +887,37 @@ object JsonCodecMaker {
887887
def getClassInfo(tpe: TypeRepr): ClassInfo = classInfos.getOrElseUpdate(tpe, {
888888
case class FieldAnnotations(partiallyMappedName: String, transient: Boolean, stringified: Boolean)
889889

890-
def getPrimaryConstructor(tpe: TypeRepr): Symbol = tpe.classSymbol match
891-
case Some(sym) if sym.primaryConstructor.exists => sym.primaryConstructor
892-
case _ => fail(s"Cannot find a primary constructor for '$tpe'")
893-
894-
def hasSupportedAnnotation(m: Symbol): Boolean =
895-
m.annotations.exists(a => a.tpe =:= TypeRepr.of[named] || a.tpe =:= TypeRepr.of[transient] ||
896-
a.tpe =:= TypeRepr.of[stringified] || (cfg.scalaTransientSupport && a.tpe =:= TypeRepr.of[scala.transient]))
890+
def hasSupportedAnnotation(m: Symbol): Boolean = m.annotations.exists { a =>
891+
val tpe = a.tpe
892+
tpe =:= TypeRepr.of[named] || tpe =:= TypeRepr.of[transient] || tpe =:= TypeRepr.of[stringified] ||
893+
(cfg.scalaTransientSupport && tpe =:= TypeRepr.of[scala.transient])
894+
}
897895

898896
def supportedTransientTypeNames: String =
899897
if (cfg.scalaTransientSupport) s"'${Type.show[transient]}' (or '${Type.show[scala.transient]}')"
900898
else s"'${Type.show[transient]}')"
901899

902900
val tpeClassSym = tpe.classSymbol.getOrElse(fail(s"Expected that ${tpe.show} has classSymbol"))
903-
val annotations = tpeClassSym.fieldMembers.collect { case m: Symbol if hasSupportedAnnotation(m) =>
904-
val name = m.name
905-
val named = m.annotations.filter(_.tpe =:= TypeRepr.of[named])
906-
if (named.size > 1) fail(s"Duplicated '${TypeRepr.of[named].show}' defined for '$name' of '${tpe.show}'.")
907-
val trans = m.annotations.filter(a => a.tpe =:= TypeRepr.of[transient] ||
908-
(cfg.scalaTransientSupport && a.tpe =:= TypeRepr.of[scala.transient]))
909-
if (trans.size > 1) warn(s"Duplicated $supportedTransientTypeNames defined for '$name' of '${tpe.show}'.")
910-
val strings = m.annotations.filter(_.tpe =:= TypeRepr.of[stringified])
911-
if (strings.size > 1) warn(s"Duplicated '${TypeRepr.of[stringified].show}' defined for '$name' of '${tpe.show}'.")
912-
if ((named.nonEmpty || strings.nonEmpty) && trans.nonEmpty)
913-
warn(s"Both $supportedTransientTypeNames and '${Type.show[named]}' or " +
914-
s"$supportedTransientTypeNames and '${Type.show[stringified]}' defined for '$name' of '${tpe.show}'.")
915-
val partiallyMappedName = namedValueOpt(named.headOption, tpe).getOrElse(name)
916-
(name, FieldAnnotations(partiallyMappedName, trans.nonEmpty, strings.nonEmpty))
917-
}.toMap
918-
val primaryConstructor = getPrimaryConstructor(tpe)
901+
var annotations = Map.empty[String, FieldAnnotations]
902+
tpeClassSym.fieldMembers.foreach {
903+
case m: Symbol if hasSupportedAnnotation(m) =>
904+
val name = m.name
905+
val named = m.annotations.count(_.tpe =:= TypeRepr.of[named])
906+
if (named > 1) fail(s"Duplicated '${TypeRepr.of[named].show}' defined for '$name' of '${tpe.show}'.")
907+
val trans = m.annotations.count(a => a.tpe =:= TypeRepr.of[transient] ||
908+
(cfg.scalaTransientSupport && a.tpe =:= TypeRepr.of[scala.transient]))
909+
if (trans > 1) warn(s"Duplicated $supportedTransientTypeNames defined for '$name' of '${tpe.show}'.")
910+
val strings = m.annotations.count(_.tpe =:= TypeRepr.of[stringified])
911+
if (strings > 1) warn(s"Duplicated '${TypeRepr.of[stringified].show}' defined for '$name' of '${tpe.show}'.")
912+
if ((named > 0 || strings > 0) && trans > 0)
913+
warn(s"Both $supportedTransientTypeNames and '${Type.show[named]}' or " +
914+
s"$supportedTransientTypeNames and '${Type.show[stringified]}' defined for '$name' of '${tpe.show}'.")
915+
val partiallyMappedName = namedValueOpt(m.annotations.find(_.tpe =:= TypeRepr.of[named]), tpe).getOrElse(name)
916+
annotations = annotations.updated(name, FieldAnnotations(partiallyMappedName, trans > 0, strings > 0))
917+
case _ =>
918+
}
919+
val primaryConstructor = tpeClassSym.primaryConstructor
920+
if (!primaryConstructor.exists) fail(s"Cannot find a primary constructor for '$tpe'")
919921

920922
def createFieldInfos(params: List[Symbol], typeParams: List[Symbol],
921923
fieldIndex: Boolean => Int): List[FieldInfo] = params.map {
@@ -978,7 +980,7 @@ object JsonCodecMaker {
978980
s"is ${fieldType.show}")
979981
}
980982
FieldInfo(symbol, mappedName, getterOrField, defaultValue, fieldType, isTransient, isStringified, fieldIndex(isTransient))
981-
}.toList
983+
}
982984

983985
val fieldIndex: Boolean => Int = {
984986
var i = -1
@@ -1032,8 +1034,7 @@ object JsonCodecMaker {
10321034
nudeSubtype.memberType(sym.primaryConstructor) match
10331035
case MethodType(_, _, _) => nudeSubtype
10341036
case PolyType(names, bounds, resPolyTp) =>
1035-
val targs = typeArgs(tpe)
1036-
val tpBinding = resolveParentTypeArgs(sym, tpeArgsFromChild, targs, Map.empty)
1037+
val tpBinding = resolveParentTypeArgs(sym, tpeArgsFromChild, typeArgs(tpe), Map.empty)
10371038
val ctArgs = names.map { name =>
10381039
tpBinding.getOrElse(name, fail(s"Type parameter $name of $sym can't be deduced from " +
10391040
s"type arguments of ${tpe.show}. Please provide a custom implicitly accessible codec for it."))

0 commit comments

Comments
 (0)