Skip to content

Commit e3ad01f

Browse files
committed
refactor(macros): remove magnolia dependency for Scala 3, use native macros
Complete migration from magnolia to native Scala 3 macros in zio-json: - Rewrite encoder with FieldEncoder abstraction, wire explicitNulls/explicitEmptyCollections - Wire explicitEmptyCollections.decoding in decoder with missingValueDecoder pattern - Add >64 field StringMatrix split support and ADT name distinctness validation - Fix constructProduct for parameterless Scala 3 enum cases - Fix encoder indent handling (indent_ for field values) - Remove magnolia1_3 dependency from build.sbt - Split platform-specific test files into scala-2.x/scala-3.x for cross-compilation - Create test-data subproject for data model files (fixes cyclic macro deps) - Bump -Xss to 8M in .jvmopts and CI (native macro inline expansion needs more stack) - Add mima binary compat filters for removed magnolia internals (pre-1.0) - Fix benchmark imports for relocated TestUtils
1 parent 632e6df commit e3ad01f

47 files changed

Lines changed: 5551 additions & 887 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
name: CI
22

33
env:
4-
JDK_JAVA_OPTIONS: -XX:+PrintCommandLineFlags -Xms6G -Xmx6G -Xss4M -XX:+UseG1GC -XX:ReservedCodeCacheSize=512M -XX:NonProfiledCodeHeapSize=256M # JDK_JAVA_OPTIONS is _the_ env. variable to use for modern Java
5-
SBT_OPTS: -XX:+PrintCommandLineFlags -Xms6G -Xmx6G -Xss4M -XX:+UseG1GC -XX:ReservedCodeCacheSize=512M -XX:NonProfiledCodeHeapSize=256M # Needed for sbt
4+
JDK_JAVA_OPTIONS: -XX:+PrintCommandLineFlags -Xms6G -Xmx6G -Xss8M -XX:+UseG1GC -XX:ReservedCodeCacheSize=512M -XX:NonProfiledCodeHeapSize=256M # JDK_JAVA_OPTIONS is _the_ env. variable to use for modern Java
5+
SBT_OPTS: -XX:+PrintCommandLineFlags -Xms6G -Xmx6G -Xss8M -XX:+UseG1GC -XX:ReservedCodeCacheSize=512M -XX:NonProfiledCodeHeapSize=256M # Needed for sbt
66

77
on:
88
pull_request:

‎.jvmopts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
-Xmx6g
2-
-Xss2m
2+
-Xss8m
33
-XX:+UseG1GC
44
-XX:InitialCodeCacheSize=512m
55
-XX:ReservedCodeCacheSize=512m

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ High **performance** is achieved by decoding JSON directly from the input source
171171
172172
Best in class **security** is achieved with an aggressive *early exit* strategy that avoids costly stack traces, even when parsing malformed numbers. Malicious (and badly formed) payloads are rejected before finishing reading.
173173
174-
**Fast compilation** and **future-proofing** is possible thanks to [Magnolia](https://propensive.com/opensource/magnolia/) which allows us to generate boilerplate in a way that will survive the exodus to Scala 3. `zio-json` is internally implemented using a [`java.io.Reader`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Reader.html) / [`java.io.Writer`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Writer.html)-like interface, which is making a comeback to center stage in Loom.
174+
**Fast compilation** and **future-proofing**: For Scala 2, derivation uses [Magnolia](https://propensive.com/opensource/magnolia/). For Scala 3, derivation uses native Scala 3 macros. `zio-json` is internally implemented using a [`java.io.Reader`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Reader.html) / [`java.io.Writer`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Writer.html)-like interface, which is making a comeback to center stage in Loom.
175175
176176
**Simplicity** is achieved by using well-known software patterns and avoiding bloat. The only requirement to use this library is to know about Scala's encoding of typeclasses, described in [Functional Programming for Mortals](https://leanpub.com/fpmortals/read#leanpub-auto-functionality).
177177

‎build.sbt‎

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import BuildHelper.*
2-
import com.typesafe.tools.mima.core.Problem
2+
import com.typesafe.tools.mima.core.*
33
import com.typesafe.tools.mima.core.ProblemFilters.exclude
44
import com.typesafe.tools.mima.plugin.MimaKeys.mimaPreviousArtifacts
55
import explicitdeps.ExplicitDepsPlugin.autoImport.moduleFilterRemoveValue
@@ -36,7 +36,7 @@ addCommandAlias("prepare", "fmt")
3636

3737
addCommandAlias(
3838
"testJVM",
39-
"zioJsonJVM/test; zioJsonYaml/test; zioJsonInteropHttp4s/test; zioJsonGolden/test; zioJsonInteropRefinedJVM/test"
39+
"zioJsonTestData/compile; zioJsonJVM/test; zioJsonYaml/test; zioJsonInteropHttp4s/test; zioJsonGolden/test; zioJsonInteropRefinedJVM/test"
4040
)
4141

4242
addCommandAlias(
@@ -90,7 +90,8 @@ lazy val zioJsonRoot = project
9090
zioJsonInteropScalaz7x.js,
9191
zioJsonInteropScalaz7x.jvm,
9292
zioJsonInteropScalaz7x.native,
93-
zioJsonGolden
93+
zioJsonGolden,
94+
zioJsonTestData
9495
)
9596

9697
val circeVersion = "0.14.15"
@@ -107,7 +108,7 @@ lazy val zioJson = crossProject(JSPlatform, JVMPlatform, NativePlatform)
107108
// as per @fommil, optimization slows things down.
108109
scalacOptions -= "-opt:l:inline",
109110
scalacOptions -= "-opt-inline-from:zio.internal.**",
110-
Test / scalacOptions ++= {
111+
scalacOptions ++= {
111112
if (scalaVersion.value == Scala3)
112113
Vector("-Yretain-trees", "-Xmax-inlines:128")
113114
else
@@ -130,9 +131,7 @@ lazy val zioJson = crossProject(JSPlatform, JVMPlatform, NativePlatform)
130131
libraryDependencies ++= {
131132
CrossVersion.partialVersion(scalaVersion.value) match {
132133
case Some((3, _)) =>
133-
Seq(
134-
"com.softwaremill.magnolia1_3" %%% "magnolia" % "1.3.18"
135-
)
134+
Seq.empty
136135
case _ =>
137136
Seq(
138137
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
@@ -235,9 +234,34 @@ lazy val zioJson = crossProject(JSPlatform, JVMPlatform, NativePlatform)
235234
inConfig(Jmh)(org.scalafmt.sbt.ScalafmtPlugin.scalafmtConfigSettings(Jmh)),
236235
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"),
237236
mimaBinaryIssueFilters ++= Seq(
238-
exclude[Problem]("zio.json.CaseObjectDecoder.*") // FIXME: false negative reported by mima
237+
exclude[Problem]("zio.json.CaseObjectDecoder.*"),
238+
exclude[MissingClassProblem]("zio.json.CaseObjectDecoder"),
239+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.join"),
240+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.split"),
241+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.subtypes$default$2"),
242+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.getParams$default$4"),
243+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.getParams_$default$5"),
244+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.getParams__$default$6"),
245+
exclude[MissingTypesProblem]("zio.json.DeriveJsonDecoder$"),
246+
exclude[MissingClassProblem]("zio.json.DeriveJsonDecoder$ArraySeq"),
247+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.join"),
248+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.split"),
249+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.subtypes$default$2"),
250+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.getParams$default$4"),
251+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.getParams_$default$5"),
252+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.getParams__$default$6"),
253+
exclude[MissingTypesProblem]("zio.json.DeriveJsonEncoder$"),
254+
exclude[MissingClassProblem]("zio.json.JsonDecoderDerivation"),
255+
exclude[MissingClassProblem]("zio.json.JsonDecoderDerivation$ArraySeq"),
256+
exclude[MissingClassProblem]("zio.json.JsonEncoderDerivation"),
257+
exclude[MissingClassProblem]("zio.json.macros$package"),
258+
exclude[MissingClassProblem]("zio.json.macros$package$")
239259
)
240260
)
261+
.jvmSettings(
262+
Test / unmanagedClasspath += Attributed.blank((zioJsonTestData / Compile / classDirectory).value),
263+
Jmh / unmanagedClasspath ++= (Test / fullClasspath).value
264+
)
241265
.jsSettings(
242266
mimaBinaryIssueFilters ++= Seq(
243267
exclude[Problem]("zio.JsonPackagePlatformSpecific.*"),
@@ -424,3 +448,19 @@ lazy val docs = project
424448
|""".stripMargin
425449
)
426450
.enablePlugins(WebsitePlugin)
451+
452+
lazy val zioJsonTestData = project
453+
.in(file("zio-json-test-data/jvm"))
454+
.settings(
455+
name := "zio-json-test-data",
456+
scalaVersion := Scala3,
457+
publish / skip := true,
458+
mimaPreviousArtifacts := Set(),
459+
scalacOptions ++= Seq("-Yretain-trees", "-Xmax-inlines:128"),
460+
Compile / unmanagedClasspath ++= (LocalProject("zioJsonJVM") / Compile / fullClasspath).value,
461+
libraryDependencies ++= Seq(
462+
"io.circe" %% "circe-core" % circeVersion,
463+
"io.circe" %% "circe-generic" % circeVersion,
464+
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.38.6"
465+
)
466+
)

‎docs/index.mdx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ In order to use this library, we need to add the following line in our `build.sb
3131
libraryDependencies += "dev.zio" %% "zio-json" % "@VERSION@"
3232
```
3333

34-
For cross-platform projects with Scala.js and Scala Native need to replace `%%` operator by `%%%`,
35-
and optionally when using `java.time.ZoneId` and `java.time.ZonedDateTime` types need to add
34+
For cross-platform projects with Scala.js and Scala Native need to replace `%%` operator by `%%%`,
35+
and optionally when using `java.time.ZoneId` and `java.time.ZonedDateTime` types need to add
3636
the dependency on the latest version of Timezone DB:
3737

3838
```scala
@@ -269,7 +269,7 @@ Extreme **performance** is achieved by decoding JSON directly from the input sou
269269

270270
Best in class **security** is achieved with an aggressive *early exit* strategy that avoids costly stack traces, even when parsing malformed numbers. Malicious (and badly formed) payloads are rejected before finishing reading.
271271

272-
**Fast compilation** and **future-proofing** is possible thanks to [Magnolia](https://propensive.com/opensource/magnolia/) which allows us to generate boilerplate in a way that will survive the exodus to Scala 3. `zio-json` is internally implemented using a [`java.io.Reader`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Reader.html) / [`java.io.Writer`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Writer.html)-like interface, which is making a comeback to center stage in Loom.
272+
**Fast compilation** and **future-proofing**: For Scala 2, derivation uses [Magnolia](https://propensive.com/opensource/magnolia/). For Scala 3, derivation uses native Scala 3 macros. `zio-json` is internally implemented using a [`java.io.Reader`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Reader.html) / [`java.io.Writer`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Writer.html)-like interface, which is making a comeback to center stage in Loom.
273273

274274
**Simplicity** is achieved by using well-known software patterns and avoiding bloat. The only requirement to use this library is to know about Scala's encoding of typeclasses, described in [Functional Programming for Mortals](https://leanpub.com/fpmortals/read#leanpub-auto-functionality).
275275

zio-json/jvm/src/test/scala/zio/json/data/geojson/GeoJSON.scala renamed to zio-json-test-data/jvm/src/main/scala/zio/json/data/geojson/GeoJSON.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ package handrolled {
120120
// custom decoders and is not a requirement to use the JsonDecoder[GeoJSON]
121121
// custom decoder (below) which is necessary to avert a DOS attack.
122122
implicit lazy val zioJsonJsonDecoder: JsonDecoder[Geometry] =
123-
new JsonDecoder.AbstractJsonDecoder[Geometry] {
123+
new JsonDecoder[Geometry] {
124124
import zio.json._
125125
import JsonDecoder.JsonError
126126
import internal._
@@ -270,7 +270,7 @@ package handrolled {
270270
// potentially complex and even skipping over it is expensive... it's a bit
271271
// of a corner case.
272272
implicit lazy val zioJsonJsonDecoder: JsonDecoder[GeoJSON] =
273-
new JsonDecoder.AbstractJsonDecoder[GeoJSON] {
273+
new JsonDecoder[GeoJSON] {
274274
import zio.json._
275275
import JsonDecoder.JsonError
276276
import internal._

zio-json/jvm/src/test/scala/zio/json/data/googlemaps/GoogleMaps.scala renamed to zio-json-test-data/jvm/src/main/scala/zio/json/data/googlemaps/GoogleMaps.scala

File renamed without changes.

zio-json/jvm/src/test/scala/zio/json/data/twitter/Twitter.scala renamed to zio-json-test-data/jvm/src/main/scala/zio/json/data/twitter/Twitter.scala

File renamed without changes.

‎zio-json/jvm/src/jmh/scala/zio/json/GeoJSONBenchmarks.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import com.github.plokhotnyuk.jsoniter_scala.core._
66
import com.github.plokhotnyuk.jsoniter_scala.macros._
77
import io.circe
88
import zio.json.GeoJSONBenchmarks._
9-
import zio.json.TestUtils._
9+
import zio.jsontest.TestUtils._
1010
import zio.json.data.geojson.handrolled._
1111
import org.openjdk.jmh.annotations._
1212

‎zio-json/jvm/src/jmh/scala/zio/json/GoogleMapsAPIBenchmarks.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import java.util.concurrent.TimeUnit
55
import com.github.plokhotnyuk.jsoniter_scala.core._
66
import com.github.plokhotnyuk.jsoniter_scala.macros._
77
import io.circe
8-
import zio.json.TestUtils._
8+
import zio.jsontest.TestUtils._
99
import zio.json.data.googlemaps._
1010
import org.openjdk.jmh.annotations._
1111
import zio.json.GoogleMapsAPIBenchmarks._

0 commit comments

Comments
 (0)