Skip to content

Commit a6488aa

Browse files
committed
refactor(macros): remove magnolia for Scala 3 + optimize macro compile time
Replace magnolia with native Scala 3 macros and optimize compile-time performance: Macro consolidation (16 invocations per DeriveJsonCodec.gen -> ~5): - Single-pass ProductMeta/SumMeta extraction replaces 9 separate inline helpers - DeriveJsonCodec.gen shares metadata between encoder and decoder derivation - Replace inline tuple recursion with macro-generated lists Generic type and edge case support: - constructProduct handles generic types via TypeApply on constructor - fieldAccessors uses Select.unique (public accessor) not Select (private field) - summonLeafEncoders/Decoders resolve applied types for generic sealed hierarchies - Default values handle type parameters correctly - Fix outer accessor issue for locally-defined types - Fixes for Either[A,B], parametric case classes, type aliases, local enums - Fix >64-field alias split off-by-one (splitIndex boundary) - Fix isEnumeration gating to match Scala 2 (check derived CaseObjectDecoder) - Restore allocation-free NestedWriter state machine from Scala 2 New codecs: - Add built-in JsonEncoder[Unit] and JsonDecoder[Unit] (encodes as {}) Feature parity with magnolia: - FieldEncoder with explicitNulls/explicitEmptyCollections - missingValueDecoder pattern, >64 field split, ADT name validation - Multi-level sealed hierarchy support via leaf type flattening - Config propagation through nested derivations - Null-peek defaults handling matching Scala 2 behavior Build changes: - Remove magnolia1_3 dependency for Scala 3 - Bump -Xss to 8M in .jvmopts and CI - Add mima binary compat filters
1 parent aae8dc8 commit a6488aa

18 files changed

Lines changed: 1725 additions & 851 deletions

File tree

.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:

.github/workflows/scala-steward.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Setup sbt
2020
uses: sbt/setup-sbt@v1
2121
- name: Scala Steward
22-
uses: scala-steward-org/scala-steward-action@v2.96.0
22+
uses: scala-steward-org/scala-steward-action@v2.95.0
2323
with:
2424
github-app-id: ${{ secrets.SCALA_STEWARD_GITHUB_APP_ID }}
2525
github-app-installation-id: ${{ secrets.SCALA_STEWARD_GITHUB_APP_INSTALLATION_ID }}

.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: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,3 @@
1-
[//]: # (This file was autogenerated using `zio-sbt-website` plugin via `sbt generateReadme` command.)
2-
[//]: # (So please do not edit it manually. Instead, change "docs/index.md" file or sbt setting keys)
3-
[//]: # (e.g. "readmeDocumentation" and "readmeSupport".)
4-
5-
# ZIO JSON
6-
7-
[ZIO Json](https://github.com/zio/zio-json) is a fast and secure JSON library with tight ZIO integration.
8-
9-
[![Production Ready](https://img.shields.io/badge/Project%20Stage-Production%20Ready-brightgreen.svg)](https://github.com/zio/zio/wiki/Project-Stages) ![CI Badge](https://github.com/zio/zio-json/workflows/CI/badge.svg) [![Sonatype Releases](https://img.shields.io/maven-central/v/dev.zio/zio-json_2.13.svg?label=Sonatype%20Release)](https://central.sonatype.com/artifact/dev.zio/zio-json_2.13) [![Sonatype Snapshots](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fcentral.sonatype.com%2Frepository%2Fmaven-snapshots%2Fdev%2Fzio%2Fzio-json_2.13%2Fmaven-metadata.xml&label=Sonatype%20Snapshot)](https://central.sonatype.com/repository/maven-snapshots/dev/zio/zio-json_2.13/) [![javadoc](https://javadoc.io/badge2/dev.zio/zio-json_2.13/javadoc.svg)](https://javadoc.io/doc/dev.zio/zio-json_2.13) [![ZIO JSON](https://img.shields.io/github/stars/zio/zio-json?style=social)](https://github.com/zio/zio-json)
10-
11-
## Introduction
12-
13-
The goal of this project is to create the best all-round JSON library for Scala:
14-
15-
- **Performance** to handle more requests per second than the incumbents, i.e. reduced operational costs.
16-
- **Security** to mitigate against adversarial JSON payloads that threaten the capacity of the server.
17-
- **Fast Compilation** no shapeless, no type astronautics.
18-
- **Future-Proof**, prepared for Scala 3 and next-generation Java.
19-
- **Simple** small codebase, concise documentation that covers everything.
20-
- **Helpful errors** are readable by humans and machines.
21-
- **ZIO Integration** so nothing more is required.
22-
23-
## Installation
24-
25-
In order to use this library, we need to add the following line in our `build.sbt` file:
26-
27-
```scala
281
libraryDependencies += "dev.zio" %% "zio-json" % "0.10.0"
292
```
303
@@ -37,32 +10,3 @@ libraryDependencies += "io.github.cquiroz" %%% "scala-java-time-tzdb" % "latest.
3710
```
3811

3912
Continue with the [Getting Started](docs/getting-started.mdx) guide for a walkthrough of encoding and decoding JSON with ZIO JSON.
40-
41-
## Documentation
42-
43-
Learn more on the [ZIO JSON homepage](https://zio.dev/zio-json/)!
44-
45-
## Contributing
46-
47-
For the general guidelines, see ZIO [contributor's guide](https://zio.dev/contributor-guidelines).
48-
49-
## Code of Conduct
50-
51-
See the [Code of Conduct](https://zio.dev/code-of-conduct)
52-
53-
## Support
54-
55-
Come chat with us on [![Badge-Discord]][Link-Discord].
56-
57-
[Badge-Discord]: https://img.shields.io/discord/629491597070827530?logo=discord "chat on discord"
58-
[Link-Discord]: https://discord.gg/2ccFBr4 "Discord"
59-
60-
## Acknowledgement
61-
62-
- Uses [JsonTestSuite](https://github.com/nst/JSONTestSuite) to test parsing. (c) 2016 Nicolas Seriot)
63-
64-
- Uses [YourKit Java Profiler](https://www.yourkit.com/java/profiler/) for performance optimisation. ![YourKit Logo](https://www.yourkit.com/images/yklogo.png)
65-
66-
## License
67-
68-
[License](LICENSE)

build.sbt

Lines changed: 29 additions & 7 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
@@ -110,7 +110,7 @@ lazy val zioJson = crossProject(JSPlatform, JVMPlatform, NativePlatform)
110110
// as per @fommil, optimization slows things down.
111111
scalacOptions -= "-opt:l:inline",
112112
scalacOptions -= "-opt-inline-from:zio.internal.**",
113-
Test / scalacOptions ++= {
113+
scalacOptions ++= {
114114
if (scalaVersion.value == Scala3)
115115
Vector("-Yretain-trees", "-Xmax-inlines:128")
116116
else
@@ -133,9 +133,7 @@ lazy val zioJson = crossProject(JSPlatform, JVMPlatform, NativePlatform)
133133
libraryDependencies ++= {
134134
CrossVersion.partialVersion(scalaVersion.value) match {
135135
case Some((3, _)) =>
136-
Seq(
137-
"com.softwaremill.magnolia1_3" %%% "magnolia" % "1.3.23"
138-
)
136+
Seq.empty
139137
case _ =>
140138
Seq(
141139
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
@@ -238,9 +236,33 @@ lazy val zioJson = crossProject(JSPlatform, JVMPlatform, NativePlatform)
238236
inConfig(Jmh)(org.scalafmt.sbt.ScalafmtPlugin.scalafmtConfigSettings(Jmh)),
239237
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"),
240238
mimaBinaryIssueFilters ++= Seq(
241-
exclude[Problem]("zio.json.CaseObjectDecoder.*") // FIXME: false negative reported by mima
239+
exclude[Problem]("zio.json.CaseObjectDecoder.*"),
240+
exclude[MissingClassProblem]("zio.json.CaseObjectDecoder"),
241+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.join"),
242+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.split"),
243+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.subtypes$default$2"),
244+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.getParams$default$4"),
245+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.getParams_$default$5"),
246+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonDecoder.getParams__$default$6"),
247+
exclude[MissingTypesProblem]("zio.json.DeriveJsonDecoder$"),
248+
exclude[MissingClassProblem]("zio.json.DeriveJsonDecoder$ArraySeq"),
249+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.join"),
250+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.split"),
251+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.subtypes$default$2"),
252+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.getParams$default$4"),
253+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.getParams_$default$5"),
254+
exclude[DirectMissingMethodProblem]("zio.json.DeriveJsonEncoder.getParams__$default$6"),
255+
exclude[MissingTypesProblem]("zio.json.DeriveJsonEncoder$"),
256+
exclude[MissingClassProblem]("zio.json.JsonDecoderDerivation"),
257+
exclude[MissingClassProblem]("zio.json.JsonDecoderDerivation$ArraySeq"),
258+
exclude[MissingClassProblem]("zio.json.JsonEncoderDerivation"),
259+
exclude[MissingClassProblem]("zio.json.macros$package"),
260+
exclude[MissingClassProblem]("zio.json.macros$package$")
242261
)
243262
)
263+
.jvmSettings(
264+
Jmh / unmanagedClasspath ++= (Test / fullClasspath).value
265+
)
244266
.jsSettings(
245267
mimaBinaryIssueFilters ++= Seq(
246268
exclude[Problem]("zio.JsonPackagePlatformSpecific.*"),
@@ -351,7 +373,7 @@ lazy val zioJsonInteropHttp4s = project
351373
libraryDependencies ++= Seq(
352374
"org.http4s" %% "http4s-dsl" % "0.23.36",
353375
"dev.zio" %% "zio" % zioVersion,
354-
"org.typelevel" %% "cats-effect" % "3.7.1",
376+
"org.typelevel" %% "cats-effect" % "3.7.0",
355377
"dev.zio" %% "zio-interop-cats" % "23.1.0.13" % Test,
356378
"dev.zio" %% "zio-test" % zioVersion % Test,
357379
"dev.zio" %% "zio-test-sbt" % zioVersion % Test

docs/getting-started.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ Extreme **performance** is achieved by decoding JSON directly from the input sou
237237

238238
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.
239239

240-
**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.
240+
**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.
241241

242242
**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).
243243

project/plugins.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.7.0")
22
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1")
3-
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.12.1")
3+
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.12.0")
44
addSbtPlugin("com.github.cb372" % "sbt-explicit-dependencies" % "0.3.1")
55
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.6")
66
addSbtPlugin("com.github.sbt" % "sbt-header" % "5.11.0")
@@ -12,7 +12,7 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.6.2")
1212
addSbtPlugin("pl.project13.scala" % "sbt-jcstress" % "0.2.0")
1313
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.4.4")
1414
resolvers += "Sonatype Snapshots" at "https://central.sonatype.com/repository/maven-snapshots/"
15-
addSbtPlugin("dev.zio" % "zio-sbt-website" % "0.7.2")
15+
addSbtPlugin("dev.zio" % "zio-sbt-website" % "0.7.0")
1616
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.7")
1717

1818
libraryDependencies += "org.snakeyaml" % "snakeyaml-engine" % "3.1.1"

zio-json/jvm/src/test/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._

0 commit comments

Comments
 (0)